@interface MyObject:NSObject - (instancetype)init __attribute__((objc_designated_initializer)); @end
在iOS中也能夠寫成segmentfault
- (instancetype)init NS_DESIGNATED_INITIALIZER;
該屬性能夠指定類的初始化方法。指定初識化方法並非對使用者。而是對內部的現實。譬如,下面這種狀況設計
@interface MyObject:NSObject - (instancetype)initMyObject NS_DESIGNATED_INITIALIZER; - (instancetype)initMyObjectNonDesignated; @end @implementation MyObject //[8] 產生warning - (instancetype)initMyObject{ self = [super init];//[1] 沒有warning return self; } - (instancetype)initMyObjectNonDesignated { self = [self initMyObject]; return self; } @end @interface DerivedObject:MyObject - (instancetype)initMyObject2; - (instancetype)initMyObject3; - (instancetype)initMyObject4 NS_DESIGNATED_INITIALIZER; - (instancetype)initMyObject5; - (instancetype)initMyObject6 NS_DESIGNATED_INITIALIZER; - (instancetype)initMyObject7; @end @implementation DerivedObject //[9] 產生warning - (instancetype)initMyObject2{ self = [super init];//[2] 產生warning return self; } - (instancetype)initMyObject3{ self = [self initMyObjectNonDesignated];//[3] 沒有warning return self; } - (instancetype)initMyObject4{ self = [super initMyObject];//[4] 沒有warning return self; } - (instancetype)initMyObject5{ self = [self init];//[5] 沒有warning return self; } - (instancetype)initMyObject6{ self = [self initMyObject4];//[6] 產生warning return self; } - (instancetype)initMyObject7{ self = [self initMyObject];//[7] 沒有warning return self; } @end
解釋一下:code
若是是DESIGNATED_INITIALIZER的初始化方法,就必須調用父類
的DESIGNATED_INITIALIZER方法。繼承
[1]沒有warning,由於
NSObject
的init
也是DESIGNATED_INITIALIZER。[4]也一樣正確,父類的initMyObject
是DESIGNATED_INITIALIZER。因此[6]就不正確了,由於initMyObject4一樣是DESIGNATED_INITIALIZER。get
若是不是DESIGNATED_INITIALIZER的初始化方法,可是該類擁有DESIGNATED_INITIALIZER初始化方法,那麼:it
必須調用該類的DESIGNATED_INITIALIZER方法或者非DESIGNATED_INITIALIZER方法。io
不能夠調用父類的任何初始化方法。循環
[2]調用的父類的方法 不正確,改爲[5]這樣就對了 [3]調用的該類的方法(從父類繼承過來的),正確
[7]也調用的該類的方法(從父類繼承過來,但會產生其餘問題,見下面解釋)方法
若是一個類擁有DESIGNATED_INITIALIZER初始化方法,那它必須覆蓋實現父類定義的DESIGNATED_INITIALIZER初始化方法。im
[8] [9]都是由於沒有覆蓋實現父類的DESIGNATED_INITIALIZER方法
注:對於非DESIGNATED_INITIALIZER,llvm把它稱爲
Convenience intializer
。
這個attribute的目的實際上是在初始化類的實例時,不管調用關係如何複雜,必須調用一次該類的Designated intializer
(能夠有多個),對於 Designated intializer
,必須調用父類的Designated intializer
。對於父類的父類這個規則亦然,對Designated intializer
的調用一直要到根類。
對於上述例子,調用觸發順序應該爲:
DerivedObject Convenience intializer
-> 若干次其餘DerivedObject Convenience intializer
-> DerivedObject Designated intializer
-> MyObject Designated intializer
-> NSObject Designated intializer
其實llvm還漏了一些細節,看上述代碼:
- (instancetype)initMyObject3{ self = [self initMyObjectNonDesignated];//[3] 沒有warning,若是改爲super 就有warning return self; }
竟然沒有Warning!這樣的話在類會跳過該類的Designated intializer
。Holy High!從上述的解釋來看,對Convenience intializer
,llvm是沒有要求全部的Convenience intializer
必須調用Designated intializer
,但這個attribute的設計思路要求終歸要調用一次該類的Designated intializer
。
對於上述狀況,我能想到的解釋就是llvm還沒智能到能分析較爲複雜的狀況。如不考慮繼承。一個類的Convenience intializer
總會有一個會調用Designated intializer
,否則就會有循環調用的可能,因此基於這個假設,llvm沒有對Convenience intializer
調用Convenience intializer
的狀況拋出Warning,但卻漏了繼承過來的Convenience intializer
狀況。
固然這只是個人猜想。
原做寫於segmentfault 連接