Objective C 中的对象分配和初始化

Posted

技术标签:

【中文标题】Objective C 中的对象分配和初始化【英文标题】:Object allocate and init in Objective C 【发布时间】:2010-09-14 10:47:27 【问题描述】:

以下两种分配和初始化对象的方式有什么区别?

AController *tempAController = [[AController alloc] init];
self.aController = tempAController;
[tempAController release];

self.aController= [[AController alloc] init];

大多数苹果示例使用第一种方法。为什么要分配、初始化和对象,然后立即释放?

【问题讨论】:

【参考方案1】:

如果您使用的是 Xcode,它可以帮助您使用静态分析器检测此类代码。 只需点击构建>>构建和分析

这将在这些代码片段中向您显示非常有用的消息。

【讨论】:

【参考方案2】:

你也可以

@property (nonatomic, retain)AController *aController;
...
self.aController= [[AController alloc] init];
[aController release];

具有保留属性,它会以相同的方式发挥作用,但最好使用其他方式(用于保留属性),因为它不那么令人困惑,该代码看起来像您分配了一个控制器,然后它会从内存中删除,而实际上并没有,因为 setAController 保留了它。

【讨论】:

如果这是一个复制属性,那就更糟了。您将释放副本,在实例变量中留下一个死对象,并泄漏您分配和初始化的对象。 但如果它保留它确实有效。不是你应该那样做。 如果您知道该属性是一个保留属性,我实际上认为这是一个好方法。这消除了一些代码混乱。【参考方案3】:

每个对象都有一个引用计数。当它变为 0 时,对象被释放。

假设属性被声明为@property (retain)

你的第一个例子,一行一行:

    对象由alloc创建,引用计数为1。 对象被交给selfsetAController:方法,该方法向它发送retain消息(因为该方法不知道对象来自哪里),将其引用计数增加到2。 调用代码不再需要对象本身,因此它调用release,将引用计数减为1。

您的第二个示例基本上执行了第 1 步和第 2 步,但没有执行第 3 步,因此最后对象的引用计数为 2。

规则是,如果您创建了一个对象,您有责任在完成后释放它。在您的示例中,代码在设置属性后使用 tempAController 完成。如果需要保留该对象,则调用 retain 是 setter 方法的责任。

重要的是要记住,Objective-C 中的 self.property = foo; 实际上只是 [self setProperty:foo]; 的简写,setProperty: 方法将根据需要保留或复制对象。

如果属性声明为@property (copy),则该对象将被复制而不是保留。在第一个示例中,原始对象将立即被释放;在第二个示例中,原始对象的引用计数为 1,即使它应该为 0。所以您仍然希望以相同的方式编写代码。

如果该属性被声明为@property (assign),那么self 没有声明该对象的所有权,并且其他人需要保留它。在这种情况下,第一个示例将不正确。这类属性很少见,通常只用于对象委托。

【讨论】:

只有在使用 @property(retain) 声明 'aController' 时才会出现这种情况,对吧? 如果属性被声明(复制)也是正确的模式。第二种模式对于声明(分配)(或启用垃圾收集)的属性是正确的。 这是“已接受”的答案,但我强烈建议您也阅读下面 mmalc 的答案。它展示了如何以更实际的方式做到这一点(具有微小、微小、微小的性能效果)。 哇,我不知道 Objective-C 引用计数的实现如此糟糕。即使是 c++ 也可以使用 shared_ptr 做得更好(一开始就不需要分配给时间变量) @lurscher 自从被问及回答以来,发生了很多变化;如果您真的对 Objective-C 内存管理感兴趣(而不仅仅是在这里发布 snark),请阅读 Automatic Reference Counting。【参考方案4】:

正如其他人所指出的,您显示的两个代码 sn-ps 不等效(出于内存管理原因)。 至于为什么选择前者而不是后者:

后者的正确表述是

self.aController= [[[AController alloc] init] autorelease];

与前者相比,这通过使用自动释放池增加了额外的开销,并且在某些情况下会导致对象的生命周期被不必要地延长(直到自动释放池被释放),这将增加您的应用程序的内存占用。

另一个“可能”的实现(取决于示例的来源)很简单:

aController = [[AController alloc] init];

但是,强烈建议不要在 init 或 dealloc 方法之外的任何地方直接设置实例变量。在其他地方,您应该始终使用访问器方法。

这将我们带到示例代码中显示的实现:

AController *tempAController = [[AController alloc] init];
self.aController = tempAController;
[tempAController release];

这遵循最佳实践,因为:

它避免了自动释放; 它使内存管理语义立即清晰; 它使用访问器方法来设置实例变量。

【讨论】:

mmalc 是对的。但是......实际上,分配给 1 个变量的 3 行代码简直是疯了。我见过的大多数团队都使用自动发布“在一行代码中完成所有工作”的方法——开销非常小。如果您曾经在 iPhone 上遇到过会产生显着性能差异的情况,那么您可能无论如何都应该用 C 重写该代码——您进行了过多的分配。很少有团队会遵守“官方”惯例(恕我直言,这是正确的——现实世界的计划应该是明确的,但不会到口头腹泻的地步)。【参考方案5】:

要注意的另一件事是,您的示例还依赖于 aController 的 @property 定义。

如果它被定义为@property (readwrite, retain) id aController;,那么您的示例可以工作,而如果它被定义为@property (readwrite, assign) id aController;,那么对释放的额外调用将​​导致您的对象被释放。

【讨论】:

没有必要指定读写,因为它是默认值。【参考方案6】:

另请注意,您希望将代码缩减为一行是许多人使用 Autorelease 的原因:

self.aController = [[[AController alloc] init] autorelease];

虽然理论上 iPhone 的自动释放在某种程度上更昂贵(从未听到过明确的解释),因此您可能希望在将对象分配到其他地方后立即显式释放。

【讨论】:

我不认为它比 Cocoa 中的 autorelease 更昂贵。只是自动发布比发布更昂贵。所以如果你可以释放对象而不是自动释放,你应该这样做。 autorelease 更昂贵,因为它必须找到一个线程本地数据结构并添加到它(自动释放池),而 release 只是减少一个整数(引用计数)。 @benzado:是的,问题是为什么 autorelease 在 iPhone 上更贵,而不是为什么它比 release 更贵。

以上是关于Objective C 中的对象分配和初始化的主要内容,如果未能解决你的问题,请参考以下文章

NSString 初始化对象有几种方法

Objective - C NSArray不可变数组和NSMutableArray可变数组

Objective c类的初始化

java中的全局变量和静态变量是在编译时分配内存还是在加载时分配内存??

Objective-C 之字符串与数值

Objective C /iPhone:是不是可以重新初始化 NSArray?