Playing with __attributes__ (四)

objc_designated_initializer

使用方法

@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,由於NSObjectinit也是DESIGNATED_INITIALIZER。[4]也一樣正確,父類的initMyObject是DESIGNATED_INITIALIZER。因此[6]就不正確了,由於initMyObject4一樣是DESIGNATED_INITIALIZER。get

  • 若是不是DESIGNATED_INITIALIZER的初始化方法,可是該類擁有DESIGNATED_INITIALIZER初始化方法,那麼:it

  1. 必須調用該類的DESIGNATED_INITIALIZER方法或者非DESIGNATED_INITIALIZER方法。io

  2. 不能夠調用父類的任何初始化方法。循環

[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 連接

相關文章
相關標籤/搜索