close
当前位置: 物联网在线 > 技术文库 > ios >

iOS 创建对象的姿势

在写 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 最后到会调用到 convenience initializer,所以 convenience initializer 也可以叫做 final initializer。

无论我们定义何种类型的 class,给 class 中的每个 property 都赋予一个初始值是个很好的习惯,可以避免掉一些意外的 bug 产生,这也是 designated initializer 的重要职责。

在实际的项目当中,一个 class 的 property 数目可能会随着业务的增长而增加,最后的结果就是会生成越来越多的 convenience initializer。上述的 User 类,如果是 3 个 property,极端的情况下最多可以有 7 个 init 方法。Peak君在阅读代码的时候,也确实看到过有些 class 定义了一连串整整齐齐摆放的 init 方法,代码虽然看着规范,但显得啰嗦,而且每次需要肉眼搜索适合的 init 方法。

其实我们还可以用另一种姿势来 init 我们的对象。

Builder pattern

最初是在学习 Android 的时候,发现这个 builder pattern 也可以用来构建对象,而且可以很好的解决 init 方法过多难以管理的问题。先来看下如何实现,顾名思义,builder pattern 使用另一个名为 builder 的类来创建我们的目标对象,还是上面的例子,代码如下:

//UserBuilder.h @interface UserBuilder : NSObject @property (nonatomic, strong, readonly) NSNumber* userID; @property (nonatomic, strong, readonly) NSString* userName; @property (nonatomic, strong, readonly) NSString* signature; - (UserBuilder*)userID:(NSNumber*)userID; - (UserBuilder*)userName:(NSString*)userName; - (UserBuilder*)signature:(NSString*)signature; @end //UserBuilder.m @implementation UserBuilder - (UserBuilder*)userID:(NSNumber*)userID { _userID = userID; return self; } - (UserBuilder*)userName:(NSString*)userName { _userName = userName; return self; } - (UserBuilder*)signature:(NSString*)signature { _signature = signature; return self; } @end

接下来 User 的 init 方法从 Builder 中获取 property 的初始值:

//User.h @interface User : NSObject @property (nonatomic, strong, readonly) NSNumber* userID;@property (nonatomic, strong, readonly) NSString* userName;@property (nonatomic, strong, readonly) NSString* signature; - (instancetype)initWithUserBuilder:(UserBuilder*)builder; @end //User.m @implementation User - (instancetype)initWithUserBuilder:(UserBuilder*)builder { self = [super init]; if (!self) { return nil; } _userID = builder.userID; _userName = builder.userName; _signature = builder.signature; return self; } @end
(责任编辑:ioter)

用户喜欢...

10 行 Python 代码创建可视化地图

作者:renwofei423 import vincent world_countries = r...


Seeed Studio Wio Tracker让你轻松创建基于GPS的IoT项目

Seeed Studio的Wio Tracker这款无线输入输出 (Wio) Tracker为开源网关,通过跟踪几乎所有移动的目标并以无线方式上传数据,...


Python 开发者面向文档编程的正确姿势

作者:HarryZhu 概述 秦人不暇自哀,而后人哀之;后人哀之而不鉴之,亦使后人而复哀后人也! –论面向文档编程的重要性...


Seeed Studio Wio Tracker登陆贸泽 让你轻松创建基于GPS的IoT项目

 最新半导体和电子元器件的全球授权分销商贸泽电子 (Mouser Electronics) ,宣布即日起开始备货Seeed Studio的Wio Tracker。这款无线输入输出 (Wio) Tracker为开源网关,通过跟踪几乎所有移动的目标并...


三星获全息AR眼镜新专利用于创建全息图像

据外媒SlashGear报道,三星已经获批了一项“全息AR眼镜”的专利,这种眼镜可以通过3D 投影创建全息图像,此前三星已...


“积跬步致千里”——英特尔无人驾驶实验室的正确打开姿势

过去几个月来,我分享了自己对无人驾驶的一腔热忱,也表达了为什么我认为英特尔将会在自动驾驶领域取得成功。...


说说分布式文件存储系统

作者:左琴 分布式文件存储系统主要被分为三种类型:分布式文件存储、块存储、对象存储。这三种存储系统都有着...


CA Technologies发布最新API管理方案协助创建并部署微服务以支援现代应用架构

CA Technologies(NASDAQ:CA)今日发布API管理组合中全新的解决方案及增强的功能,帮助开发者、企业架构师和数字化领导...


完全理解 Python 迭代对象、迭代器、生成器

在了解Python的数据结构时,容器(container)、可迭代对象(iterable)、迭代器(iterator)、生成器(generator)、列表/集合/字典推导式(list,set,dict comprehension)众多概念参杂在一起,难免让初学者一头雾水,...


(译)Android:创建可穿戴应用 - 语音操作

添加语音处理能力(Adding Voice Capabilities)语音操作是可穿戴用户体验的重要部分,可以让用户以快捷、免提的方式执行动作。 Wear提供两种类型的语音操作: 系统提供(System-provided) 这些语...