在Objective-c中释放变量的正确方法

时间:2010-12-31 22:29:51

标签: iphone objective-c object-lifetime

我知道在Objective-c中有一种非常简单的方法来声明这样的变量:

NSArray* myArray;

@property(retain) NSArray* myArray;

@synthesize myArray;

这样你就可以在保留变量的同时使用self.myArray作为setter和getter。但是,这也可以让你做一件事,这就是避免使用dealloc。据我了解,这两行是相同的:

self.myArray = nil;
[myArray release];

我的问题是,哪一个是首选方式?是否存在其中一个可以工作而另一个不工作的情况?

编辑:对不起,我的意思是释放,而不是dealloc ......

4 个答案:

答案 0 :(得分:4)

你永远不应该自己打电话给dealloc(非常非正统的情况除外)。

您应该致电[myArray release]而不是dealloc,让释放的流程为您处理。

请查看here,了解更多关于dealloc方法的信息。

答案 1 :(得分:3)

最新消息。

使用(保留)合成属性时,执行dealloc职责的最佳方法是将属性设置为nil。我说这是“最好的”方式的原因是它确保满足财产声明所隐含的所有合同。例如,如果您的属性被声明为原子(除非您明确声明它是非原子的),保证在dealloc上使用相同的原子保证完成此属性的取消设置的唯一方法是将其设置为nil使用dealloc的财产。它还意味着它对于对象的任何键值观察都会表现正确 - 这一点非常重要,尤其是在使用Cocoa绑定时。

当为没有相应属性的(可能是私有的)实例变量进行自己的内存管理时,有几个习语。最简单但最危险的是简单地释放iVar,就像这样:

- (void)dealloc
{
    [myArray release];

    [super dealloc];
}

这将导致iVar上的保留被释放,但正如其他人所提到的那样,将使现在可能过时的指针留在潜在的,如果错误的话,可能存在指向可能存在的过时或非保留指针被解除分配的对象。接下来是另一个答案提出的习语:

- (void)dealloc
{
    [myArray release], myArray = nil;

    [super dealloc];
}

如果更加迂腐,那就更安全了:

- (void)dealloc
{
    id temp = myArray;
    myArray = nil;
    [temp release];

    [super dealloc];
}

通过在释放指向的对象之前清除iVar,这进一步限制了过时读取指针的可能性。但是,由于指令重新排序的可能性,即使这不是对所有体系结构的陈旧读取的100%保证。

虽然关于并发和内存管理的主题还有很多话要说,但一般来说,如果你有一个@synthesized属性设置器,你应该在dealloc中使用它。这样做意味着如果您更改@property的行为,则相对于@property声明,dealloc行为将自动正确。

重要说明:使用原子属性!=线程安全。 (事实上​​,如果你问我原子属性是一种浪费,但是......)有关详细信息,请参阅here

<强>更新

这最近再次投票,虽然我支持我在这里所说的关于合成保留属性的原子保证,并且原始答案中的其他一些内容本身就有价值,但我觉得有必要告诉对方故事的一面。 Dave DeLong在评论中暗示了其中一些内容,但我认为值得在主要答案中添加细节。

我坚持认为维持原子性保证的唯一方法是通过setter将属性设置为nil BUT 你不应该关心,这就是原因:如果一个对象被dealloc编辑,那意味着(如果你的对象图是正确的)那个对象应该没有生命引用。如果没有对象的生命引用,那么任何人都不可能关心清除该属性的操作的原子性保证。

我在原始答案中也提到了KVO作为在dealloc中使用setter的理由,但Dave DeLong在评论中提到了KVO作为对位。他是对的,这就是原因:同样,如果一个物体被dealloc编辑,所有KVO观察者应该已经被移除(再次,不应该有生命参考,KVO或不是)。事实上,如果情况并非如此,那么不久你就会看到一条控制台消息,告诉你你的对象已经消失,观察结果仍然存在。

简而言之,虽然你不能使dealloc中的合成setter的原子性保证等同,但它应该永远不重要(如果确实如此,那么其他东西就会被破坏。)

答案 2 :(得分:1)

每当你有一个对象的实例变量时,你必须在dealloc方法中释放它。所以在你的情况下你必须使用

- (void)dealloc
{
    [myArray release], myArray = nil;

    [super dealloc];
}

答案 3 :(得分:1)

理论上设置为零应该与释放ivar具有相同的效果。但是你永远不应该直接调用dealloc方法。在dealloc方法中,你有时会看到大卫提到的成语,特别是:

[myArray release], myArray = nil;

这样做的原因是为了避免一个非常不可能的竞争条件,即有人可能会在dealloc完成之前尝试访问已发布的对象。通过将属性分配为nil,可以使尝试访问正常失败。