@interface MyObject:NSObject - (instancetype)init __attribute__((objc_designated_initializer)); @end
在iOS中也可以写成
- (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
解释一下:
如果是DESIGNATED_INITIALIZER的初始化方法,就必须调用 父类
的DESIGNATED_INITIALIZER方法。
[1]没有warning,因为 NSObject
的 init
也是DESIGNATED_INITIALIZER。[4]也同样正确,父类的 initMyObject
是DESIGNATED_INITIALIZER。所以[6]就不正确了,因为initMyObject4同样是DESIGNATED_INITIALIZER。
如果不是DESIGNATED_INITIALIZER的初始化方法,但是该类拥有DESIGNATED_INITIALIZER初始化方法,那么:
必须调用该类的DESIGNATED_INITIALIZER方法或者非DESIGNATED_INITIALIZER方法。
不可以调用父类的任何初始化方法。
[2]调用的父类的方法 不正确,改成[5]这样就对了 [3]调用的该类的方法(从父类继承过来的),正确[7]也调用的该类的方法(从父类继承过来,但会产生其他问题,见下面解释)
如果一个类拥有DESIGNATED_INITIALIZER初始化方法,那它必须覆盖实现父类定义的DESIGNATED_INITIALIZER初始化方法。
[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
情况。
当然这只是我的猜测。