什么是NS_DESIGNATED_INITIALIZER
NS_DESIGNATED_INITIALIZER是Xcode6后之后出現(xiàn)的一個(gè)黑魔法,通過(guò)它可以讓我們充分發(fā)揮編譯器的特性(編譯時(shí)檢查,語(yǔ)法錯(cuò)誤后并給出warning),進(jìn)而幫我們找出初始化過(guò)程中可能存在的漏洞,增加代碼的健壯性,寫(xiě)出更規(guī)范的代碼。
NS_DESIGNATED_INITIALIZER 定義
定義位置
#import <objc/NSObjCRuntime.h>
定義內(nèi)容
#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
通過(guò)代碼我們可以看出首先通過(guò)GNU C的__has_attribute進(jìn)行判斷,然后才是attribute((objc_designated_initializer))定義,關(guān)于(__attribute__)
讓我們看看NSObject的init定義
- (instancetype)init
#if NS_ENFORCE_NSOBJECT_DESIGNATED_INITIALIZER
NS_DESIGNATED_INITIALIZER
#endif
;
可以看出,如果子類(lèi)沒(méi)有指定NS_DESIGNATED_INITIALIZER,則默認(rèn)把init方法作為NS_DESIGNATED_INITIALIZER,如果子類(lèi)有NS_DESIGNATED_INITIALIZER,那么init將只是一個(gè)普通的初始化方法。從Objective-C的繼承鏈我們可以看出,除了如NSProxy之外的類(lèi),幾乎都是派生自NSObject,所有init才成為了所有類(lèi)的標(biāo)配初始化函數(shù)
具體用法
1. Objective-C的默認(rèn)的init函數(shù)為init之后第一個(gè)字母必須以大寫(xiě)字母開(kāi)頭,而且函數(shù)的返回值必須是instancetype或者id類(lèi)型,否則如initobject2的命名方式無(wú)法作為NS_DESIGNATED_INITIALIZER;

如- (instancetype)initobject1函數(shù),編譯器會(huì)認(rèn)為它是一個(gè)普通的實(shí)例函數(shù),而不是初始化函數(shù),所以無(wú)法當(dāng)做指定初始化函數(shù)。
2.NS_DESIGNATED_INITIALIZER不能出現(xiàn)在函數(shù)實(shí)現(xiàn)的地方@implementation和Category中;


如圖,錯(cuò)誤信息已經(jīng)很明顯了,NS_DESIGNATED_INITIALIZER只能是在Extension和代碼定義時(shí);
3.子類(lèi)指定初始化函數(shù)必須調(diào)用父類(lèi)的指定初始化函數(shù)進(jìn)行初始化;
@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,則必須調(diào)用父類(lèi)的初始化方法進(jìn)行初始化函數(shù)init來(lái)初始化,而- (instancetype)initObject3和- (instancetype)initObject2這種非指定初始化函數(shù),則調(diào)用本類(lèi)initObject或initObject1指定初始化函數(shù)進(jìn)行初始化。
4.子類(lèi)沒(méi)有指定初始化函數(shù)時(shí),重寫(xiě)父類(lèi)指定初始化函數(shù),調(diào)用父類(lèi)的指定初始化函數(shù)進(jìn)行初始化,重寫(xiě)非指定初始化函數(shù)時(shí),調(diào)用父類(lèi)或子類(lèi)任意初始化函數(shù)都可以;
@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重寫(xiě)的- (instancetype)initObject1也可以直接調(diào)用Object的- (instancetype)initObject函數(shù)進(jìn)行初始化,但是這樣降低了可讀性。(子類(lèi)的指定初始化函數(shù)實(shí)現(xiàn)時(shí)可調(diào)用任意父類(lèi)的初始化函數(shù)進(jìn)行初始化)。而子類(lèi)在沒(méi)有非指定初始化函數(shù)的情況下,可以使用任意初始化函數(shù)進(jìn)行初始化。
5.子類(lèi)非指定初始化函數(shù)初始化時(shí),如果子類(lèi)有指定初始化函數(shù),則調(diào)用本類(lèi)的指定初始化函數(shù),反之則調(diào)用父類(lèi)的初始化函數(shù)或者(其他初始化函數(shù),最終的調(diào)用還是走的指定初始化函數(shù))。而且一旦子類(lèi)添加指定初始化函數(shù)之后,那么優(yōu)先級(jí)將高于父類(lèi);

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

而從第二張圖可以看出此時(shí)的initObject、initObject1變?yōu)榱似胀ǖ某跏蓟瘮?shù),而且initObject調(diào)用了本類(lèi)的指定初始化函數(shù)進(jìn)行初始化,initObject1則調(diào)用initObject進(jìn)行初始化。
6.一旦子類(lèi)有指定初始化函數(shù)時(shí),那么init函數(shù)就不再是指定初始化函數(shù),需手動(dòng)重寫(xiě)init函數(shù),并且init初始化時(shí)調(diào)用本類(lèi)的指定初始化函數(shù)進(jìn)行初始化;

從上圖我們可以看到帶了一個(gè)警告,那是因?yàn)樽宇?lèi)已經(jīng)有了指定初始化函數(shù),而之前我們也在NSObject總看到init函數(shù)是在沒(méi)有指定初始化函數(shù)時(shí)才作為指定初始化函數(shù),所以此時(shí)我們需要手動(dòng)實(shí)現(xiàn)init函數(shù),并通過(guò)指定初始化函數(shù)進(jìn)行初始化操作,如圖:

而SubObject中沒(méi)有報(bào)此錯(cuò)誤,那是因?yàn)樗母割?lèi)已經(jīng)替他實(shí)現(xiàn)了該函數(shù),所以它不再需要重寫(xiě)。
以上僅是對(duì)此做一個(gè)記錄,免得使用時(shí)又忘了。至于Demo, 直接從代碼中拷吧。
參考鏈接:
http://www.cnblogs.com/smileEvday/p/designated_initializer.html
https://yq.aliyun.com/articles/5847