前言
在写 iOS 代码的时候,怎么样去 new 一个新对象出来,都有一些讲究在里面。使用不同的姿势去创建对象,对后期维护所造成的影响会存在细微的差别。
init 创建
在之前一篇分析 iOS 代码耦合的文章中,提到过当我们给一个对象的 property 赋值的时候,通过 init 方法传入参数来初始化 property 会让我们的代码更可靠。
有些人在定义带 property 的 class 的时候,会这样定义:
@interface User : NSObject
@property (nonatomic, strong) NSNumber* userID;
@end
使用的时候如下:
User* user = [[User alloc] init];
user.userID = @1000;
尤其是在定义 model 的时候,很容易写出这种,先 init,而后挨个给 property 赋值的代码。这种代码的问题在于 property 对于外部是可写的,property 处于随时可能变化的状态。之前不少篇文章中都强调过 immutable 的重要性,同样对于一个 class,我们也应该优先考虑设计成 immutable 的。
initWith 创建
如果将 property 都设置成 readonly 的,或者不暴露 property,property 的赋值都通过 initWith 的方式来初始化,就可以得到一个具备 immutable 的 class 定义了,具体到上面的例子代码如下:
//User.h
@interface User : NSObject
@property (nonatomic, strong, readonly) NSNumber* userID;
- (instancetype)initWithUserID:(NSNumber*)uid;
@end
//User.m
@implementation User
- (instancetype)initWithUserID:(NSNumber*)uid {
self = [super init];
if (!self) {
return nil;
}
_userID = uid;
return self;
}
@end
userID 在 .h 文件当中是 readonly 的,userID 只有一次被赋值的机会,即在 User 的 initWith 方法中。这种方式的好处是一旦 User 对象创建完毕之后,就处于 immutable 的状态,property 都是不可修改的,安全可靠。
Designated initializer
Apple 为了方便开发者使用 init 方法,引入了一种名为 designated initializer 的 pattern。主要用来管理当一个 class 拥有多个 property 需要赋值的场景。比如上面我们的 User 类:
@interface User : NSObject
@property (nonatomic, strong, readonly) NSNumber* userID;
@property (nonatomic, strong, readonly) NSString* userName;
@property (nonatomic, strong, readonly) NSString* signature;
@end
有些场景需要初始化 userID 和 userName,而有些场景只需要初始化 userID 和 signature,所以我们需要提供多个 initWith 方法给不同的场景使用。为了管理 initWith 方法,Apple 将 init 方法分为两种类型:designated initializer 和 convenience initializer (又叫 secondary initializer) 。
designated initializer 只有一个,它会为 class 当中每个 property 都提供一个初始值,是最完整的 initWith 方法。convenience initializer 则可以有很多个,它可以选择只初始化部分的 property。convenience initializer 最后到会调用到 designated initializer,所以 designated initializer 也可以叫做 final initializer。










