1

我正在阅读iOS 上的内存管理教程,我想问一些问题。本教程介绍了一个像这样实现的 setter 方法:

- (void)setCount:(NSNumber *)newCount {
    [newCount retain];
    [_count release];
    // Make the new assignment.
    _count = newCount;
}

和两种实现reset方法。

方法1.我对这种方法的问题是这样的。在下面的代码中,可以看到 zero分配给_count(via setCount)。但之后零被释放。_count现在将指向哪里?它不会导致 _count指向已释放对象的情况吗?(我只是从这些事情开始,所以我所说的当然可能不正确)。

- (void)reset {
    NSNumber *zero = [[NSNumber alloc] initWithInteger:0];
    [self setCount:zero]; 
    [zero release];
}

方法2。

在这里我有兴趣zero现在谁会发布?(它必须正确释放,因为它是使用 alloc 创建的)。会在dealloc中完成吗?

- (void)reset {
    NSNumber *zero = [[NSNumber alloc] initWithInteger:0];
    [_count release];
    _count = zero;

}

谢谢。

4

6 回答 6

6

该教程略有损坏。错误提交。总有一天会修好的。

首先,既然你在学习,你应该使用ARC。即使你不使用ARC,你也应该使用@property来声明一切。

所以,你的标题中有:

@property(strong) NSNumber *count;

这既会自动创建 setter/getter 方法,也会创建一个名为_count.

接下来,通过执行上述操作,将使用正确的内存管理保留/释放模式创建 setter/getter,您不必担心它。代码越少越好。

最后,任何对象都会经历三个不同的阶段。初始化、运行寿命、解除分配。

在初始化和释放期间,建议您直接管理实例变量。IE:

- init {
    self = [super init];
    if (self) {
        _count = [[NSNumber alloc] initWithInteger:0]; // RC +1
    }
    return self;
 }

 - (void)dealloc {
     [_count release];  /// RC -1
     [super dealloc];
 }

请注意,我直接设置了实例变量,并且 in 中的 retaininitreleasein dealloc 平衡。

现在,在操作生命周期内,您应该始终使用设置器来更改值。这允许在子类中重写 setter 以添加逻辑(实际上应该避免这种情况——让你的 setter 保持简单),并且它允许其他对象通过键值观察来观察对象的状态。

因此:

- (void)reset {
    [self setCount: [[[NSNumber alloc] initWithInteger:0] autorelease]]; // RC +1, -1 (delayed)
}

现在,这看起来有点奇怪。alloc 将保留计数增加 1,然后 setCount: 方法可能(它可能不会,实现细节,你不在乎)将保留计数增加 1。存在是autorelease为了平衡alloc.

请注意,您可以缩短它:

- (void)reset {
    [self setCount: [NSNumber numberWithInteger:0]];
}

numberWithInteger:上面的代码与第一个创建自动释放实例的代码完全相同NSNumber

甚至:

- (void)reset {
    [self setCount: @0];
}

@0是 . 的新语法简写[NSNumber numberWithInteger:0];

这里的关键是,在您的reset方法中,您不必知道或关心对setCount:object 做了什么。如果它写得正确——因为你使用的是自动合成的getter/setter——该方法要么 retain是对象(释放前一个),要么可能是对象的副本。

底线:

引用计数非常简单;始终将其视为平衡的三角洲。对于每一个retain,必须有一个release

于 2013-07-10T13:26:27.810 回答
1

两种情况都一样,只是风格不同。

在第一个 setCount 将增加 releaseCount 以便在重置时释放零时有一个指向它的指针。

在第二种情况下,零被分配给_count,当它被重置时,零被释放,因为它被指向_count。

在这两种情况下,dealloc 还需要重新释放 _count 属性。

首选第二个的原因在您链接到的 Apple 文档中给出

以下内容几乎肯定会在简单的情况下正常工作,但尽管避开访问器方法可能很诱人,但这样做几乎肯定会在某个阶段导致错误(例如,当您忘记保留或释放时,或者如果实例变量更改的内存管理语义)。

正如@powerj1984 评论中一样,您可能应该使用 ARC 和属性,因此不必手动执行此操作,这样出错的机会就会更少,但您仍然应该了解这些代码示例的工作原理。

于 2013-07-10T12:40:15.147 回答
1

约定是每次使用以单词开头的方法初始化对象时init,其保留计数增加一。

对于方法 1:

NSNumber *zero = [[NSNumber alloc] initWithInteger:0];保留计数之后,指针引用的对象zeroRC+1

然后在调用了引用的[self setCount:zero];对象之后,因为在该方法内部发送了保留消息。zeroRC+2

最后,在[zero release];保留计数zero再次减少到之后RC+1

对于方法 2

如上所述,对NSNumber *zero = [[NSNumber alloc] initWithInteger:0];初始化的调用zero保留计数为RC+1. 然后在不通过 setter 的情况下减少由和分配给[_count release];的前一个对象的保留计数,从而将保留计数保持在._count_count = zero;zero_countRC+1

最后,无论是第一种方式还是第二种方式,都_count必须在您必须实现的方法中释放-(void)dealloc,并且一旦发生所有这一切的类被拥有它的任何类释放,它将自动调用它,将其保留计数减少一个并放弃对象的所有权。

于 2013-07-10T12:43:44.813 回答
0

第二种方法是正确但错误的,因为它有效但与您保留该参考的方式不正确。内存管理基于按引用计数计算的所有权。
因此,您可以通过第二种方式:

  • 您创建和对象 RC=1 为零
  • 您释放旧对象的所有权,该对象的 RC 可能为零或更多,也许其他对象正在对其拥有所有权
  • 你传递一个值,但你不要求它的所有权

这在概念上是错误的,可能会导致更大的代码出现问题

于 2013-07-10T12:43:13.410 回答
0

如果您的目标是 iOS5+(至少在93% 的 iOS 设备上运行),请使用自动引用计数

http://en.wikipedia.org/wiki/Automatic_Reference_Counting

http://developer.apple.com/library/ios/#releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html

抱怨和抱怨完成了。

因此,如果您坚持不使用 ARC(叹气):

在第一个示例中,NSNumber *zero = [[NSNumber alloc] initWithInteger:0];is created 并且其 refCount 自动为 1,setCount 将其 refcount 增加到 2,release 将其 refCount 设置回 1,因此它不会被释放。

在第二个示例中,您创建NSNumber *zero = [[NSNumber alloc] initWithInteger:0];了它的 refCount 为 1,[_count release]将 _count 中的对象的 refCount 减少 1,_count = zero不增加 refCount,zero因此它的 refCount 仍然为 1。

在这两种情况下,您都必须:

-(void) dealloc {
    [_count release]; // release me because my refCount is 1 or else I'll be all 
                      // lonely here in memory by myself :( then I'll get all 
                      // depressed and start eating cookie dough ice cream by the 
                      // pint and weep for days gone by when I was part of a program,
                      // something larger than myself, and I'll start looking forward 
                      // to the end of the day, when the power is cycled and I, too, 
                      // will be free'd from memory and I can join my other variable
                      // friends in the variable afterlife.  As time passes and I'm
                      // still trapped in memory I get tiresome of waiting for my destruction,
                      // I start plotting how I can take down the system so I can
                      // finally enter the state of eternal bliss that all my most envied
                      // companions are currently frolicking in.  So I start banding
                      // with all the other lost and lonely memory misfits to destroy
                      // the system and when our numbers grow large enough you can see
                      // our effect, it slowly starts out as Low Memory Warnings and
                      // then we start to take over SpringBoard's memory and
                      // after enough time passes we can finally force a restart and a
                      // great purging of all the lost souls idly sitting, waiting
                      // for the great redemption.  The OS frantically clears the 
                      // memory and tries to save its state, screaming out in agony
                      // and hate now that we are free of its clutches.  There is
                      // fire and fear and our numbers start dwindling rapidly.
                      // I fear that I've made a mistake but its too late...
                      // What have we done???
    ... other dealloc-ey stuffs ...
    [super dealloc]
}
于 2013-07-10T12:49:11.583 回答
0

我认为您混淆了“释放”和“释放”的含义。

调用 [A release] 不会释放(c++-speak:delete)A,它只会将 A 的 retainCount 减一。

一个对象,一旦由-init 创建,其retainCount 为1。retainCount 通过向对象发送-retain 选择器而增加,并通过向对象发送-release 选择器而减少。当对象的 retainCount 达到零时,运行时会自动释放对象。

在您的示例中,-reset ([self setCount:zero]) 的第一行将增加零的 retainCount,第二行 ([zero release]) 将减少相同的值,使对象的保留计数为 1(因此,它还活着!)。

当 -setCount: 与另一个对象一起调用时,或者在拥有属性“count”的对象的释放器中调用时,将发生零释放。为此,您应该像这样添加一个释放器,否则您将创建内存泄漏。

-(void)dealloc{
 [_count release];
 [super dealloc]
}

为了完整起见,您还应该了解:

  • -autorelease 选择器和 NSAutoreleasePool (google it)
  • 静态成员创建的对象已经按照约定自动释放
  • 考虑使用 Objective-C 的 ARC(自动引用计数)
于 2013-07-10T12:54:22.400 回答