在上一篇文章《利用__attribute__特性提高 APP 的鲁棒性》中,提及了NS_DESIGNATED_INITIALIZER属性。该属性表示指定初始化方法,今天的主题就是聊一聊NSObject的初始化流程。
初始化分为两步:
1. 分配内存
2. 初始化对象的成员变量
![93538d22ff9ab7970b56494a256e35d0.png](https://i-blog.csdnimg.cn/blog_migrate/b073cbcfea6c7a6a16e914c483497e59.jpeg)
分配内存
如果有C语言开发经验的人,malloc之后,一般需要memset一下。但在Objective-C里面,可不是这样的。
![b661fb71e4008551aeba5a47e9eb4a36.png](https://i-blog.csdnimg.cn/blog_migrate/acca391d4d5a1dafcd715525737d26ff.png)
苹果这段话表达了三个意思:
1. 开辟一个内存空间
2. isa变量和引用计数
3. 将所有的变量初始值置为0
初始化
从上面的描述中,仅仅alloc出来的对象还不能直接使用,还需要调用初始化函数。
以下是NSObject的初始化函数声明:
![020c15ae372d3a743adcf0e10d90f6bc.png](https://i-blog.csdnimg.cn/blog_migrate/4521bcc63a80a3a28d6388c3602ab0e3.jpeg)
而这里的init函数是NS_DESIGNATED_INITIALIZER的,也就是指定了初始化函数。
与指定初始化函数(designated initializer)对应的是便利初始化函数(convenience initializers)
接下来分析一个具体的例子:
#import <Foundation/Foundation.h>
这里声明了一个指定初始化函数initWithCount和一个便利初始化函数:initWithCount:year。
![43882aedddcfb7e7e8b0e93476140e67.png](https://i-blog.csdnimg.cn/blog_migrate/4351f7f37bd8c46537218935c0290970.jpeg)
这里给出了三个警告,第一个警告表示父类的指定初始化函数没有找到。第二个和第三个警告表示不能直接调用父类的初始化函数
解决方案如下:
![5542a96766dc3360ed2361351d3b7feb.png](https://i-blog.csdnimg.cn/blog_migrate/0e2afe2a8a41e45dbf40750945546424.jpeg)
从以上实现可以得出如下结论:
1. 如果子类有指定初始化函数,那么一定要实现所有父类的指定初始化函数,实现后的函数需调用本类的指定初始化函数
规则解读:P2PBomb需要实现父类的init函数,而且这里面必要还要调用[self initWithCount:0]
2. 如果子类有指定初始化函数,那么便利初始化函数必须调用自己的其它初始化函数(指定初始化函数或者其他便利初始化函数),不能调用super的初始化函数。
规则解读:在便利初始化函数中调用[self initWithCount:0],如果在这里调用[super init]会存在告警
3. 如果子类有指定初始化函数,子类指定初始化函数必须调用父类的指定初始化函数
规则解读:在指定初始化函数中,调用了[super init].
从以上规则,不难推导出另外一条规则,就是最终都要调用指定初始化函数。
再从苹果官方提供的图来理解一下这几条规则:
![1dd00fa1c920fa8d65d8500325a0f61b.png](https://i-blog.csdnimg.cn/blog_migrate/2865f3c7a51ccae0634a5f11eddd75fd.jpeg)
使用场景
也许会有人说,这个东西在实际代码中能用到吗?貌试没有用呢。OK,那我们举一个使用场景:
使用场景:定义NS_DESIGNATED_INITIALIZER,大多是不想让调用者调用父类的初始化函数,只希望通过该类指定的初始化进行初始化。
改进优化
如果父类的指定初始化函数比较多,一一实现一把比较繁琐,这时NS_UNAVAILABLE可以派上用场了。
#import <Foundation/Foundation.h>
如果调用者使用init 初始化,编译器就会给出一个编译错误。
使用NS_UNAVAILABLE后,就不需要在.m中重写父类初始化函数了。如果要允许调用者使用init就需要在.m中重写父类的初始化函数,如最开始提到的,否则就会报警告。注意,如果使用new来创建对象的话,即使init被声明为NS_UNAVAILABLE,编译器不会提示告警和错误。
限制:
NS_DESIGNATED_INITIALIZER不能出现在Category中。
参考文献:
Object Allocation
Adopting Modern Objective-C