转载

Playing with __attributes__ (四)

objc_designated_initializer

使用方法

@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,因为 NSObjectinit 也是DESIGNATED_INITIALIZER。[4]也同样正确,父类的 initMyObject 是DESIGNATED_INITIALIZER。所以[6]就不正确了,因为initMyObject4同样是DESIGNATED_INITIALIZER。

  • 如果不是DESIGNATED_INITIALIZER的初始化方法,但是该类拥有DESIGNATED_INITIALIZER初始化方法,那么:

  1. 必须调用该类的DESIGNATED_INITIALIZER方法或者非DESIGNATED_INITIALIZER方法。

  2. 不可以调用父类的任何初始化方法。

[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 情况。

当然这只是我的猜测。

正文到此结束
Loading...