Objective-C的初始化方法总结

 

分析:

OC中对象的init方法有两种:
指定初始化器(Designated Initializers )、便利初始化器(Convenience Initializers)
Designated Initializers能保证初始化所有需要初始化的数据,RD需要自己保证,可能有多个不同方法,代表从不同源/不同方式进行初始化,比如UIView就有initWithFrame和initWithNib分别代表从代码和Nib初始化。
Convenience Initializers字面意思便利初始化,它内部一定会调用Designated Initializers
总结:所有的初始化方法的根本都是调用Designated Initializers。
 
那么如何告诉编译器某个方法是Designated Initializers呢?
NS_DESIGNATED_INITIALIZER
这是个宏,含义就是一个编译器指令,告诉编译器这个是指定初始化方法
#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
举个🌰,一个ViewController,继承于UIViewController:
#import <UIKit/UIKit.h>
 
@interface ViewController : UIViewController
 
// 我自己定义的init方法,我希望ViewController这个类初始化只能用这个方法
- (instancetype)initWithSomething NS_DESIGNATED_INITIALIZER;
 
@end
在.h里我声明了initWithSomething方法,并且指定了NS_DESIGNATED_INITIALIZER宏。
 
OC有几条规定:
  1. 子类的Designated Initializers必须调用父类的Designated Initializers
  2. 子类如果声明了新的Designated Initializers方法,那么有两种方案:
2a. 要在子类中复写父类的所有Designated Initializers,内部都导向到子类新的Designated Initializers方法去。
2b. 在子类中直接禁用父类其它的Designated Initializers,使用NS_UNAVAILABLE这个宏。
 
分析一下为啥要这么规定:
对于上面的1:如果不强制子类的Designated Initializers内部调用父类的Designated Initializers,那么子类的Designated Initializers有可能只调用了父类的某个Convenience Initializers,而Convenience Initializers无法保证初始化父类所有的成员变量!所以Apple官方从源头上进行遏制。
对于2也是类似的:因为子类中依然可以调用父类的任意Designated Initializers方法,比如上面的自定义ViewController类,如果我这样调用父类UIViewController的Designated Initializers方法:
[ViewController alloc] initWithNibName:xx bundle:yy]
那么显然子类ViewController中的成员变量没有进行初始化,这是不能接受的。
 
分析的透彻一点,子类一旦声明了新的Designated Initializers方法,那么父类的Designated Initializers方法其实就相当于“退化”了,因为它们无法保证初始化子类的所有成员变量,已经无法满足“初始化方法”的定义。而如果你仍想继续使用父类的某些Designated Initializers方法,你就复写,并且在其内部调用到新的Designated Initializers方法;而如果你不想再用父类的某些Designated Initializers方法了,那更简单,在子类.h里直接禁用它们。
 

最终实现:

ViewController.h:
@interface ViewController : UIViewController
// init是NSObject的初始化方法,我不想让别人用,在.h里直接禁用
- (instancetype)init NS_UNAVAILABLE;
// 这个initWithNibName方法是UIViewController的,我也不想让别人用,禁用
- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil NS_UNAVAILABLE;
// 我自己定义的Designated Initializers方法
- (instancetype)initWithSomething NS_DESIGNATED_INITIALIZER;
 
@end
ViewController.m:
@implementation ViewController
 
#pragma mark - Designated Initializers
// 唯一Designated Initializers
- (instancetype)initWithSomething {
// 注意:这里调用了父类的Designated Initializers:initWithNibName方法
self = [super initWithNibName:nil bundle:nil];
if (self) {
self.view.backgroundColor = UIColor.whiteColor;
}
return self;
}
 
#pragma mark - Convenience Initializers
// 这个initWithCoder方法我还想用,那么复写,在内部调用initWithSomething即可
- (instancetype)initWithCoder:(NSCoder *)coder
{
return [self initWithSomething];
}
对于上面的ViewController类,Designated Initializers方法只有一个:initWithSomething
Convenience Initializers方法也是一个:initWithCoder
posted @ 2021-05-10 15:23  NeoZy  阅读(544)  评论(0编辑  收藏  举报