关于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