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。
对象被交给self
的setAController:
方法,该方法向它发送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 中的对象分配和初始化的主要内容,如果未能解决你的问题,请参考以下文章
Objective - C NSArray不可变数组和NSMutableArray可变数组