我有時會告訴自己。我們的類也經(jīng)常這樣做。但在 Objective-C 中,有幾個地方這樣做是有風險的:init 和 dealloc。
本文是Objective-C 中的代碼氣味系列文章中的一篇。
在 Objective-C 的 init 和 dealloc 代碼中,我經(jīng)??吹竭@樣的代碼。我舉一個簡單的例子。你能找出問題所在嗎?
- (id)initWithFoo:(id)foo
{
self = [super init];
if (self)
self.something = foo;
return self;
}
- (void)dealloc
{
self.something = nil;
[super dealloc];
}
提示:是那些self.。它們?nèi)菀鬃屓苏`以為是簡單的作業(yè)。但請記住,點符號隱藏著信息。
讓我們避開點符號,再試一次:
- (id)initWithFoo:(id)foo
{
self = [super init];
if (self)
[self setSomething:foo];
return self;
}
- (void)dealloc
{
[self setSomething:nil];
[super dealloc];
}
現(xiàn)在你看到了嗎?
當給自己的信息有氣味的
向自己發(fā)送信息通常沒有問題。但有兩個地方要避免:
- 創(chuàng)建對象時,以及
- 對象被銷毀時。
在這兩個時間段,物體處于一種有趣的、介于兩者之間的狀態(tài)。它缺乏完整性。在這兩個時間段調(diào)用方法是一種代碼缺陷。為什么呢?因為每個方法在對對象進行操作時都應(yīng)保持不變。下面是對象在方法中流動時的自我一致性(self-consistency)概述:
- 開始:假設(shè)對象是自我一致性(self-consistency)的。
- 進行中:對象狀態(tài)處于變化中。
- 結(jié)束:恢復(fù)對象自我一致性(self-consistency)的不變性。
提示:不變性使你保持清醒。
我并沒有為此做出非常規(guī)的嘗試。蘋果公司有一份關(guān)于實用內(nèi)存管理的文檔,其中有一節(jié)的標題是 "不要在初始化方法和 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 aninitmethod 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
解決方法很簡單:在 Objective-C 的 init 和 dealloc 方法中,直接訪問實例變量,而不是通過屬性。在非 ARC 代碼中,檢查屬性屬性是否保留或分配。然后編寫與直接訪問相匹配的代碼。例如,如果某個屬性是保留屬性,默認支持 ivar _something,那么我們的代碼就會變成:
- (id)initWithFoo:(id)foo
{
self = [super init];
if (self)
_something = [foo retain];
return self;
}
- (void)dealloc
{
[_something release];
[super dealloc];
}
在 init/dealloc 中向 self 發(fā)送信息時仍能正常工作
在說過 "避免在 init 和 dealloc 中向 self 發(fā)送信息 "之后,我現(xiàn)在想緩和一下這種說法。畢竟有兩個地方是可以這樣做的:
- 在
init階段的最后階段,以及 - 在 dealloc 開始時
這是因為在這兩個地方,對象具有自一致性( self-consistency)。在 init 中,所有 ivars 都已建立。在 dealloc 中,沒有一個 ivars 被銷毀。
但您仍需謹慎行事,并認識到自己在對象生命周期中的位置。僅僅創(chuàng)建一個對象并不能開始任何繁重的工作。創(chuàng)建和銷毀都要輕便快捷。
譯自 Jon Reid 的 Objective-C init: Why It’s Helpful to Avoid Messages to self
侵刪