![](https://i.iter01.com/images/59328f3309ad48a11b8cd8e7d71304ccbc0c8955679b5b1f90c7d318c2499e46.jpg)
物件初始化有兩種方式:[class new]
與 [[class alloc] init]
對於後者,有分配和初始化的過程,alloc
從應用程式的虛擬地址空間上為該物件分配足夠的記憶體,並且將新物件的引用計數加1、將物件的成員變數初始為零,init
會做真正的初使化工作,為物件的例項變數賦予合理有用的值。
一般不推薦使用[class new]
,而推薦使用[[class alloc] new]
,檢視原始碼分析一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
+ new { id newObject = (*_alloc)((Class)self, 0); Class metaClass = self->isa; if (class_getVersion(metaClass) > 1) return [newObject init]; else return newObject; } //而 alloc/init 像這樣: + alloc { return (*_zoneAlloc)((Class)self, 0, malloc_default_zone()); } - init { return self; } |
發現[class new]
預設呼叫 [newObject init]
方法,那麼我們無法使用自定義的初始化方法,多了更多的侷限性。那麼[class alloc] init]
會更方便, 當然[class alloc] init]
的設計也是由於歷史的原因。
為啥這麼寫?
1 2 3 4 5 6 7 |
- (instancetype)init { if (self = [super init]) { // Custom initialization } return self; } |
我們知道alloc
返回一個有效的未初始化的物件例項。對於self
是alloc
返回的指標,同時可以在所有的方法作用域內訪問。
但是對於 super
,它只是一個”編譯器指示符”,告訴編譯器在父類中搜尋方法的實現。
優先呼叫[super init]
是為了使超類完成它們自己的初始化工作。
那麼 if (self = [super init])
又是做啥?
這裡是擔心父類初始化失敗,如果初始化一個物件失敗,就會返回nil
,當返回nil
的時候self = [super init]
測試的主體就不會再繼續執行。如果不這樣做,你可能會操作一個不可用的物件,它的行為是不可預測的,最終可能會導致你的程式崩潰。
理解 Self & Super
看到網上一道經典的題目:
1 2 3 4 5 6 7 8 9 10 11 12 |
@implementation Son : Father - (id)init { self = [super init]; if (self) { NSLog(@"%@", NSStringFromClass([self class])); NSLog(@"%@", NSStringFromClass([super class])); } return self; } @end |
self
表示當前這個類的物件,而super
是一個編譯器標示符,和self
指向同一個訊息接受者。在本例中,無論是[self class]
還是[super class]
,接受訊息者都是Son
物件,但super
與self
不同的是,self
呼叫class
方法時,是在子類Son
中查詢方法,而super
呼叫class
方法時,是在父類Father
中查詢方法。
當呼叫[self class]
方法時,會轉化為objc_msgSend
函式,這個函式定義如下:
1 |
id objc_msgSend(id self, SEL op, ...) |
這時候就開始了訊息傳遞和轉發的過程,會先從Cache
中查詢方法,然後當前類,如果還是查詢不到會去父類,直至NSObject
類
對於NSObject
類中,- (Class)class
的實現如下:
1 2 3 |
- (Class)class { return object_getClass(self); } |
所以列印結果為Son
當呼叫[super class]
方法時,會轉化為objc_msgSendSuper
,這個函式定義如下:
1 |
id objc_msgSendSuper(struct objc_super *super, SEL op, ...) |
objc_msgSendSuper
函式第一個引數super
的資料型別是一個指向objc_super
的結構體
1 2 3 4 |
struct objc_super { __unsafe_unretained id receiver; __unsafe_unretained Class super_class; }; |
結構體包含兩個成員,第一個是receiver
,表示類的例項。第二個成員是記錄當前類的父類是什麼,會優先從Father
這個類裡去找- (Class)class
,然後進行訊息傳遞的過程。
會發現不管是self
、還是super
指向訊息接受者是一樣的,並且經過訊息傳遞,最終處理訊息的方法都是NSObject
中的- (Class)class
方法。