初始化方法內使用self有什么壞處茶鹃?

初始化方法內使用self有什么壞處?

托腮思考

場景描述

iOS初始化方法包括系統(tǒng)默認的和自定義的艰亮,常見系統(tǒng)初始化方法有init, initWithFrame:, initWithNibName:bundle:等闭翩,自定義則是各式各樣。日常iOS項目開發(fā)過程中迄埃,我們經(jīng)常在類的初始化方法中初始化接下來類需要用到的一些必要的數(shù)據(jù)或界面疗韵。初始化方法內使用self的場景大致有兩種,一是self調用方法蕉汪,諸如:[self doSomething]流译,二是屬性初始化,諸如:self.property = xxx者疤。樣式大體如下:

@interface HHAnimal : NSObject

@property (nonatomic, strong) NSString *name;

@end

@implementation HHAnimal

- (instancetype)init {
    self = [super init];
    if (self) {
        self.name = @"HH";
        [self doSomething];
    }
    return self;
}

- (void)doSomething {
}

@end

那么福澡,為什么不建議在初始化方法內self.property = xxx or [self doSomething]類似代碼呢?

問題分析

當使用self.property = xxx時驹马,系統(tǒng)會幫我們做這兩件事情:

  1. 方法調用革砸。[self setProperty:xxx]
  2. KVO。發(fā)送該屬性的變化給監(jiān)聽者

那么窥翩,合并一下[self doSomething]业岁,初始化調用self大體就分為方法調用和KVO。這兩件事情在一般情況下不會有問題寇蚊,但在類在初始化的過程中笔时,類處于一種部分初始化的狀態(tài),此時很有可能出現(xiàn)錯誤仗岸。因為執(zhí)行的方法體或者監(jiān)聽屬性值變化的對象會認為當前執(zhí)行過程是完全初始化的穩(wěn)定狀態(tài)允耿,當類執(zhí)行體使用了還未初始化的數(shù)據(jù)時,就可能發(fā)生數(shù)據(jù)錯亂扒怖,程序異辰衔或者crash。

下面我們舉例子說明~

舉例佐證

為了更好地說明盗痒,如下代碼中蚂蕴,假設我們有一個HHAnimal類,有三個屬性俯邓,age年齡骡楼,name動物名字(目前是可讀), attrDescription表示用于展示的帶顏色的名字(只讀屬性),它是一個計算變量---根據(jù)年齡變化稽鞭,名字的顏色不一樣鸟整。

@interface HHAnimal : NSObject

@property (nonatomic, assign) NSUInteger age;
@property (nonatomic, readonly) NSString *name;
@property (nonatomic, readonly) NSAttributedString *attrDescription;

@end

@implementation HHAnimal

- (instancetype)init {
    self = [super init];
    if (self) {
        self.age = 12;
        _name = @"HH";
    }
    return self;
}

- (void)updateAttrDescription {
    NSDictionary *attrs = nil;
    if (self.age < 18) {
        attrs = @{NSForegroundColorAttributeName: [UIColor greenColor]};
    } else {
        attrs = @{NSForegroundColorAttributeName: [UIColor yellowColor]};
    }
    _attrDescription = [[NSAttributedString alloc] initWithString:self.name attributes:attrs];
}

@end

我們從出現(xiàn)錯誤的概率,一級級的往上遞增‰蹋現(xiàn)在我們希望在初始化后篮条,attrDescription變量也被初始化。第一種添加方法 (直接在設置完默認年齡后調用[self updateAttrDescription]):

- (instancetype)init {
    self = [super init];
    if (self) {
        self.age = 12;
        [self updateAttrDescription];
        _name = @"HH";
    }
    return self;
}

有人會說吩抓,這種方法弱爆了涉茧,看我的:

- (void)setAge:(NSUInteger)age {
    _age = age;
    [self updateAttrDescription];
}
耶~

這種方式高級一些,將attrDescription跟年齡關聯(lián)起來琴拧。嗯降瞳,不錯。但還是crash了,因為在執(zhí)行[self updateAttrDescription]時挣饥,name為nil除师,而[NSAttributedString initWithString:attributes:]方法調用時若string為nil,蘋果爸爸直接給崩了扔枫。

那為什么你會這么寫呢汛聚?其中原因之一可能是因為你不知道該系統(tǒng)方法不能將nil作為參數(shù),另外一個重要的點是你很明顯看到name在初始化方法中已經(jīng)被賦值了短荐,這樣就不存在nil的問題倚舀。

這種顯而易見的場景在日常開發(fā)過程中,我們會很快發(fā)現(xiàn)忍宋。但痕貌,假設有一個子類HHHuman繼承了HHAnimal,它只能看到父類的.h文件(且聲明name是具有初始值的)糠排。若HHHuman的實現(xiàn)內重寫了agesetter方法舵稠,并將name當做已初始化的一個變量使用的話,就可能引入崩潰等問題入宦。

[UIViewController view]

除了上面介紹的一些例子哺徊,日常開發(fā)中一個更復雜也常見的例子要屬UIViewController的property--view。假設在初始化的過程中寫了self.view乾闰,如下所示:

@implementation ViewController

- (instancetype)initWithOrderId:(NSString *)orderId {
    self = [super init];
    if (self) {
        NSLog(@"%@", self.view);
        _orderId = orderId;
    }
    return self;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    [HHNetUtil requestWithOrderId:_orderId completionBlock:...];
}

這樣寫落追,會有什么奇怪的事情發(fā)生呢?正常的UIViewController的初始化->界面展現(xiàn)流程是:

init -> loadView -> viewDidLoad -> viewWillApear:

調用self.view后流程是:

init(loadView -> viewDidLoad) -> viewWillAppear:

在初始化中調用self.view后涯肩,系統(tǒng)會自動觸發(fā)loadView, viewDidLoad流程轿钠。在init方法<strong>期間</strong>會依次調用loadView -> viewDidLoad,此時初始化的數(shù)據(jù)還未完成病苗,viewDidLoad方法很可能拿到空數(shù)據(jù)(比如上述代碼根據(jù)init初始化后的orderId來請求訂單相關數(shù)據(jù))谣膳,程序就會異常。除此之外铅乡,我們可能在創(chuàng)建完UIViewController后,并不是想立即展現(xiàn)它烈菌,而是希望采用懶加載在想展示時阵幸,再進行viewDidLoad過程中創(chuàng)建界面、數(shù)據(jù)處理或請求資源芽世。

舉了不少例子說明不宜在初始化中使用self挚赊,那么還有方法需要注意嗎?

dealloc內最好也別用self.property = xxx

跟初始化類似济瓢,dealloc方法也是一個過程性荠割,“不穩(wěn)定”的方法。這里的不穩(wěn)定指的是當前過程是一個不完全的狀態(tài),不完全初始化蔑鹦,不完全釋放(析構)夺克。

dealloc除了會遇到初始化中介紹的問題以外,還經(jīng)常出現(xiàn)KVO機制引發(fā)的異常嚎朽。當一個對象A監(jiān)聽對象B的屬性C時铺纽,如果在B的dealloc內調用B.C = nil,就會觸發(fā)A中的監(jiān)聽方法哟忍。此時如果再使用B中的一些屬性或者方法狡门,B處于半釋放狀態(tài),就會引起一些異常的奇奇怪怪的問題锅很。所以其馏,此時使用_C = nil更加安全。

結語

本文先分析了不建議在初始化中使用self的原因爆安,并通過多個例子進行證明叛复,最后衍生出dealloc也最好別用的推論。雖然大多情況下鹏控,大家使用self沒有出錯(在你一直都能保證調用的方法及屬性的設置不會影響其他代碼情況下)致扯,但風險就在那里,self在当辐,它就在抖僵。

說到這里,大家很可能會想到Objective-C的繼承者---Swift類的兩段式構造過程缘揪,它更安全耍群、規(guī)范。Swift通過這兩個構造過程保證了所有需要初始化的屬性都能初始化完找筝,避免了因屬性沒有初始值導致之后使用過程不可預知狀況蹈垢。不太清楚又想了解Swift類的兩段式構造過程的同學可以戳官方中文教程

最后袖裕,感謝大家的閱讀曹抬,有問題請指正,大家相互討論~

參考文章

Initializing a property, dot notation
Should I refer to self.property in the init method with ARC?
Objective-C init: Why It’s Helpful to Avoid Messages to self
Practical Memory Management

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末急鳄,一起剝皮案震驚了整個濱河市谤民,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌疾宏,老刑警劉巖张足,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異坎藐,居然都是意外死亡为牍,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來碉咆,“玉大人抖韩,你說我怎么就攤上這事∫魇牛” “怎么了帽蝶?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長块攒。 經(jīng)常有香客問我励稳,道長,這世上最難降的妖魔是什么囱井? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任驹尼,我火速辦了婚禮,結果婚禮上庞呕,老公的妹妹穿的比我還像新娘新翎。我一直安慰自己,他們只是感情好住练,可當我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布地啰。 她就那樣靜靜地躺著,像睡著了一般讲逛。 火紅的嫁衣襯著肌膚如雪亏吝。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天盏混,我揣著相機與錄音蔚鸥,去河邊找鬼。 笑死许赃,一個胖子當著我的面吹牛止喷,可吹牛的內容都是我干的。 我是一名探鬼主播混聊,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼弹谁,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了句喜?” 一聲冷哼從身側響起僵闯,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎藤滥,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體社裆,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡拙绊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片标沪。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡榄攀,死狀恐怖,靈堂內的尸體忽然破棺而出金句,到底是詐尸還是另有隱情檩赢,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布违寞,位于F島的核電站贞瞒,受9級特大地震影響,放射性物質發(fā)生泄漏趁曼。R本人自食惡果不足惜军浆,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望挡闰。 院中可真熱鬧乒融,春花似錦、人聲如沸摄悯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽奢驯。三九已至申钩,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間叨橱,已是汗流浹背典蜕。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留罗洗,地道東北人愉舔。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像伙菜,于是被迫代替她去往敵國和親轩缤。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內容