關于objc_designated_initializer介紹

什么是NS_DESIGNATED_INITIALIZER

NS_DESIGNATED_INITIALIZER是Xcode6后之后出現的一個黑魔法,通過它可以讓我們充分發揮編譯器的特性(編譯時檢查,語法錯誤后并給出warning),進而幫我們找出初始化過程中可能存在的漏洞,增加代碼的健壯性,寫出更規范的代碼。

NS_DESIGNATED_INITIALIZER 定義

定義位置
#import <objc/NSObjCRuntime.h>
定義內容
#ifndef NS_DESIGNATED_INITIALIZER
#if __has_attribute(objc_designated_initializer)
#define NS_DESIGNATED_INITIALIZER attribute((objc_designated_initializer))
#else
#define NS_DESIGNATED_INITIALIZER
#endif
#endif
通過代碼我們可以看出首先通過GNU C的__has_attribute進行判斷,然后才是attribute((objc_designated_initializer))定義,關于(__attribute__)

讓我們看看NSObject的init定義
- (instancetype)init
#if NS_ENFORCE_NSOBJECT_DESIGNATED_INITIALIZER
NS_DESIGNATED_INITIALIZER
#endif
;

可以看出,如果子類沒有指定NS_DESIGNATED_INITIALIZER,則默認把init方法作為NS_DESIGNATED_INITIALIZER,如果子類有NS_DESIGNATED_INITIALIZER,那么init將只是一個普通的初始化方法。從Objective-C的繼承鏈我們可以看出,除了如NSProxy之外的類,幾乎都是派生自NSObject,所有init才成為了所有類的標配初始化函數

具體用法
1. Objective-C的默認的init函數為init之后第一個字母必須以大寫字母開頭,而且函數的返回值必須是instancetype或者id類型,否則如initobject2的命名方式無法作為NS_DESIGNATED_INITIALIZER;
image.png

如- (instancetype)initobject1函數,編譯器會認為它是一個普通的實例函數,而不是初始化函數,所以無法當做指定初始化函數。

2.NS_DESIGNATED_INITIALIZER不能出現在函數實現的地方@implementation和Category中;
image.png
image.png

如圖,錯誤信息已經很明顯了,NS_DESIGNATED_INITIALIZER只能是在Extension和代碼定義時;

3.子類指定初始化函數必須調用父類的指定初始化函數進行初始化;
@interface Object()

- (instancetype)initObject NS_DESIGNATED_INITIALIZER;

- (instancetype)initObject1 NS_DESIGNATED_INITIALIZER;

- (instancetype)initObject2;

@end

@implementation Object

- (instancetype)initObject;
{
    return self = [super init];
}

- (instancetype)initObject1;
{
    return self = [super init];
}

- (instancetype)initObject2;
{
    return self = [self initObject3];
}


- (instancetype)initObject3;
{
    return self = [self initObject1];
}

@end

如- (instancetype)initObject和- (instancetype)initObject1,則必須調用父類的初始化方法進行初始化函數init來初始化,而- (instancetype)initObject3和- (instancetype)initObject2這種非指定初始化函數,則調用本類initObject或initObject1指定初始化函數進行初始化。

4.子類沒有指定初始化函數時,重寫父類指定初始化函數,調用父類的指定初始化函數進行初始化,重寫非指定初始化函數時,調用父類或子類任意初始化函數都可以;
@interface Object()

- (instancetype)initObject NS_DESIGNATED_INITIALIZER;

- (instancetype)initObject1 NS_DESIGNATED_INITIALIZER;

- (instancetype)initObject2;

@end

@implementation Object

- (instancetype)initObject;
{
    return self = [super init];
}

- (instancetype)initObject1;
{
    return self = [super init];
}

- (instancetype)initObject2;
{
    return self = [self initObject3];
}


- (instancetype)initObject3;
{
    return self = [self initObject1];
}

@end


@interface SubObject()


@end

@implementation SubObject

- (instancetype)initObject;
{
    return self = [super initObject];
}

- (instancetype)initObject1;
{
    return self = [super initObject1];
}

- (instancetype)initObject3;
{
    return self = [self initObject];
}

- (instancetype)initObject2;
{
    return self = [super initObject2];
//    return self = [self initObject];
//    return self = [self initObject3];
//    return self = [super initObject];
}

@end

上述代碼SubObject重寫的- (instancetype)initObject1也可以直接調用Object的- (instancetype)initObject函數進行初始化,但是這樣降低了可讀性。(子類的指定初始化函數實現時可調用任意父類的初始化函數進行初始化)。而子類在沒有非指定初始化函數的情況下,可以使用任意初始化函數進行初始化。

5.子類非指定初始化函數初始化時,如果子類有指定初始化函數,則調用本類的指定初始化函數,反之則調用父類的初始化函數或者(其他初始化函數,最終的調用還是走的指定初始化函數)。而且一旦子類添加指定初始化函數之后,那么優先級將高于父類;
image.png

如圖,此時的initObject、initObject1等都已不再是指定初始化函數

image.png

而從第二張圖可以看出此時的initObject、initObject1變為了普通的初始化函數,而且initObject調用了本類的指定初始化函數進行初始化,initObject1則調用initObject進行初始化。

6.一旦子類有指定初始化函數時,那么init函數就不再是指定初始化函數,需手動重寫init函數,并且init初始化時調用本類的指定初始化函數進行初始化;
image.png

從上圖我們可以看到帶了一個警告,那是因為子類已經有了指定初始化函數,而之前我們也在NSObject總看到init函數是在沒有指定初始化函數時才作為指定初始化函數,所以此時我們需要手動實現init函數,并通過指定初始化函數進行初始化操作,如圖:

image.png

而SubObject中沒有報此錯誤,那是因為它的父類已經替他實現了該函數,所以它不再需要重寫。

以上僅是對此做一個記錄,免得使用時又忘了。至于Demo, 直接從代碼中拷吧。

參考鏈接:
http://www.cnblogs.com/smileEvday/p/designated_initializer.html
https://yq.aliyun.com/articles/5847

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容