我有時(shí)會(huì)告訴自己成畦。我們的類也經(jīng)常這樣做。但在 Objective-C 中涝开,有幾個(gè)地方這樣做是有風(fēng)險(xiǎn)的:init
和 dealloc
循帐。
本文是Objective-C 中的代碼氣味系列文章中的一篇。
在 Objective-C 的 init
和 dealloc
代碼中舀武,我經(jīng)尘逶。看到這樣的代碼。我舉一個(gè)簡(jiǎn)單的例子奕剃。你能找出問題所在嗎衷旅?
- (id)initWithFoo:(id)foo
{
self = [super init];
if (self)
self.something = foo;
return self;
}
- (void)dealloc
{
self.something = nil;
[super dealloc];
}
提示:是那些self.
捐腿。它們?nèi)菀鬃屓苏`以為是簡(jiǎn)單的作業(yè)。但請(qǐng)記住柿顶,點(diǎn)符號(hào)隱藏著信息茄袖。
讓我們避開點(diǎn)符號(hào),再試一次:
- (id)initWithFoo:(id)foo
{
self = [super init];
if (self)
[self setSomething:foo];
return self;
}
- (void)dealloc
{
[self setSomething:nil];
[super dealloc];
}
現(xiàn)在你看到了嗎嘁锯?
當(dāng)給自己的信息有氣味的
向自己發(fā)送信息通常沒有問題宪祥。但有兩個(gè)地方要避免:
- 創(chuàng)建對(duì)象時(shí),以及
- 對(duì)象被銷毀時(shí)家乘。
在這兩個(gè)時(shí)間段蝗羊,物體處于一種有趣的、介于兩者之間的狀態(tài)仁锯。它缺乏完整性耀找。在這兩個(gè)時(shí)間段調(diào)用方法是一種代碼缺陷。為什么呢业崖?因?yàn)槊總€(gè)方法在對(duì)對(duì)象進(jìn)行操作時(shí)都應(yīng)保持不變野芒。下面是對(duì)象在方法中流動(dòng)時(shí)的自我一致性(self-consistency)概述:
- 開始:假設(shè)對(duì)象是自我一致性(self-consistency)的。
- 進(jìn)行中:對(duì)象狀態(tài)處于變化中双炕。
- 結(jié)束:恢復(fù)對(duì)象自我一致性(self-consistency)的不變性狞悲。
提示:不變性使你保持清醒。
我并沒有為此做出非常規(guī)的嘗試妇斤。蘋果公司有一份關(guān)于實(shí)用內(nèi)存管理的文檔摇锋,其中有一節(jié)的標(biāo)題是 "不要在初始化方法和 dealloc 中使用訪問方法"。
Don’t Use Accessor Methods in Initializer Methods and dealloc
The only places you shouldn’t use accessor methods to set an instance variable are in initializer methods and
dealloc
. To initialize a counter object with a number object representing zero, you might implement aninit
method as follows:- init { self = [super init]; if (self) { _count = [[NSNumber alloc] initWithInteger:0]; } return self; }
To allow a counter to be initialized with a count other than zero, you might implement an initWithCount: method as follows:
- initWithCount:(NSNumber *)startingCount { self = [super init]; if (self) { _count = [startingCount copy]; } return self; }
Since the Counter class has an object instance variable, you must also implement a dealloc method. It should relinquish ownership of any instance variables by sending them a release message, and ultimately it should invoke super’s implementation:
- (void)dealloc { [_count release]站超; [super dealloc]荸恕; }
Objective-C init/dealloc:拯救 ivars
解決方法很簡(jiǎn)單:在 Objective-C 的 init
和 dealloc
方法中,直接訪問實(shí)例變量顷编,而不是通過屬性。在非 ARC 代碼中剑刑,檢查屬性屬性是否保留或分配媳纬。然后編寫與直接訪問相匹配的代碼。例如施掏,如果某個(gè)屬性是保留屬性钮惠,默認(rèn)支持 ivar _something
,那么我們的代碼就會(huì)變成:
- (id)initWithFoo:(id)foo
{
self = [super init];
if (self)
_something = [foo retain];
return self;
}
- (void)dealloc
{
[_something release];
[super dealloc];
}
在 init/dealloc 中向 self 發(fā)送信息時(shí)仍能正常工作
在說過 "避免在 init
和 dealloc
中向 self
發(fā)送信息 "之后七芭,我現(xiàn)在想緩和一下這種說法素挽。畢竟有兩個(gè)地方是可以這樣做的:
- 在
init
階段的最后階段,以及 - 在 dealloc 開始時(shí)
這是因?yàn)樵谶@兩個(gè)地方狸驳,對(duì)象具有自一致性( self-consistency)预明。在 init
中缩赛,所有 ivars 都已建立。在 dealloc
中撰糠,沒有一個(gè) ivars 被銷毀酥馍。
但您仍需謹(jǐn)慎行事,并認(rèn)識(shí)到自己在對(duì)象生命周期中的位置阅酪。僅僅創(chuàng)建一個(gè)對(duì)象并不能開始任何繁重的工作旨袒。創(chuàng)建和銷毀都要輕便快捷。
譯自 Jon Reid 的 Objective-C init: Why It’s Helpful to Avoid Messages to self
侵刪