目标C中的对象分配和初始化
以下两种分配和初始化对象的方式有什么区别?
AController *tempAController = [[AController alloc] init]; self.aController = tempAController; [tempAController release];
和
self.aController= [[AController alloc] init];
苹果的大多数示例都使用第一种方法。为什么要分配,初始化和对象然后立即释放?
解决方案
每个对象都有一个参考计数。当它变为0时,对象将被释放。
假设该属性被声明为" @property(保留)":
第一个示例,逐行:
- 该对象由alloc创建,其引用计数为1.
- 将对象移交给"自己"的" setAController:"方法,该方法向其发送"保留"消息(因为该方法不知道对象来自何处),将其引用计数增加到2.
- 调用代码不再需要对象本身,因此它调用
release
,将引用计数减为1.
第二个示例基本上执行步骤1和2,但不执行步骤3,因此最后该对象的引用计数为2.
规则是,如果我们创建一个对象,则在使用完对象后,我们有责任释放它。在示例中,代码在设置属性后使用tempAController完成。如果需要保留该对象,则setter方法负责调用"保留"。
重要的是要记住,Objective-C中的" self.property = foo;"实际上只是" [self setProperty:foo];"的简写,并且" setProperty:"方法将根据需要保留或者复制对象。
如果该属性被声明为@property(copy),则该对象将被复制而不是保留。在第一个示例中,原始对象将立即被释放;在第二个示例中,即使原始对象的引用计数为0,也将为1. 因此,我们仍然希望以相同的方式编写代码。
如果该属性被声明为" @property(assign)",则" self"不会声明该对象的所有权,而其他人则需要保留它。在这种情况下,第一个示例将是不正确的。这类属性很少见,通常仅用于对象委托。
还请注意,我们希望将代码缩减为一行,这就是为什么许多人使用"自动发布"的原因:
self.aController = [[[AController alloc] init] autorelease];
尽管从理论上讲,iPhone的自动释放成本更高(从来没有听到过清楚的解释为什么),因此我们可能希望在将对象分配到其他位置后立即释放。
要注意的另一件事是,示例还取决于aController的@property定义。
如果将其定义为@property(读写,保留)id aController;
,则示例有效;而如果将其定义为`@property(读写,赋值)id aController;',则额外的release调用将导致对象被释放。
正如其他人指出的那样,我们显示的两个代码段不相同(出于内存管理的原因)。
关于为什么选择前者而不是后者:
后者的正确表述是
self.aController= [[[AController alloc] init] autorelease];
与前者相比,这通过使用自动释放池增加了额外的开销,并且在某些情况下会导致不必要地延长对象的生存期(直到释放自动释放池),这将增加应用程序的内存占用。
另一个"可能的"实现(取决于示例的来源)很简单:
aController = [[AController alloc] init];
但是,强烈建议不要在init或者dealloc方法之外的任何地方直接设置实例变量。在其他地方,应始终使用访问器方法。
然后,我们将转到示例代码中所示的实现:
AController *tempAController = [[AController alloc] init]; self.aController = tempAController; [tempAController release];
这遵循最佳实践,因为:
- 避免自动释放;
- 它使内存管理语义立即清晰明了;
- 它使用访问器方法设置实例变量。
你也可以
@property (nonatomic, retain)AController *aController; ... self.aController= [[AController alloc] init]; [aController release];
具有保留属性,它的作用方式相同,但最好使用另一种方法(保留属性),因为它不那么令人困惑,该代码使它看起来像我们分配了aController,然后实际上从内存中删除这不是因为setAController保留了它。