iOS创建对象的不同姿势详解
发布时间 - 2026-01-10 23:04:11 点击率:次前言

在写 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。
无论我们定义何种类型的 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
如果要创建 User 对象,则按照这种方式:
UserBuilder* builder = [[[[UserBuilder new] userName:@"peak"] userID:@1000] signature:@"roll"]; User* user = [[User alloc] initWithUserBuilder:builder];
这样我们避免了书写多个 init 方法,同样 User 对象也是 immutable 的,也做到了只在 init 方法中做一次赋值操作,每个场景都可以按照自己的需求初始化部分 property,当然最后我们需要在 initWithUserBuilder 中为每一个 property 赋值, initWithUserBuilder 扮演的角色类似于 designated initializer。
追求代码美感的同学可能发现了, UserBuilder 的创建语法很丑陋,多个 [ ] 套嵌使用。为了让代码更好看一些,我们也可以使用 block 来创建:
User* user = [User userWithBlock:^(UserBuilder* builder) {
builder.userName = @"peak";
builder.userID = @1000;
builder.signature = YES;
}];
builder pattern 在 Android 平台使用的比较多,我在 iOS 平台上鲜少有看到使用的场景。builder pattern 的不足之处也比较明显,需要另外定义一个 builder 类,多写一些代码(property 基本都重复写了一遍)。个人觉得,在 property 数量较多,初始化的场景也比较多的时候,在 iOS 上使用 builder pattern 也会是个不错的方案。
designated initializer vs builder pattern,这二者之间的不同其实很好的体现了语言本身的差异性。学习过 java 的同学就能明白,在 java 的世界中,一切都是可以被封装成对象的,使用 java 的时候,经常要定义各式各样的辅助类来完成某个任务,好处是封装度高,类职责划分粒度小,缺点是类太多,有时候会为了封装而封装,某些场景代码反而不够直观。
经读者反馈,原来这篇文章的主题已经被写过了。看过之后发现比我写的更全面,推荐大家阅读,传送门。
总结
以上就是这篇文章的全部内容了,本文简单梳理了下创建对象的不同姿势,希望对大家有些帮助。如果有疑问大家可以留言交流。
# ios创建对象
# ios
# class
# 创建对象
# 创建单例对象
# IOS给xcode工程关联pod的实例详解
# IOS 开发之对象为空的判断(nil、null)详解
# iOS 对象属性详细介绍
# IOS 开发之Object-C中的对象详解
# iOS关联对象示例详解
# 多个
# 很好
# 是个
# 是在
# 较多
# 这篇文章
# 自己的
# 都是
# 看着
# 都有
# 我在
# 太多
# 也会
# 过了
# 就能
# 最多
# 有很多
# 可以用
# 很容易
# 一遍
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
网站图片在线制作软件,怎么在图片上做链接?
北京网站制作的公司有哪些,北京白云观官方网站?
如何用JavaScript实现文本编辑器_光标和选区怎么处理
如何自定义建站之星网站的导航菜单样式?
Laravel Session怎么存储_Laravel Session驱动配置详解
如何快速选择适合个人网站的云服务器配置?
浅谈redis在项目中的应用
中山网站制作网页,中山新生登记系统登记流程?
edge浏览器无法安装扩展 edge浏览器插件安装失败【解决方法】
python中快速进行多个字符替换的方法小结
Laravel用户认证怎么做_Laravel Breeze脚手架快速实现登录注册功能
使用Dockerfile构建java web环境
如何用搬瓦工VPS快速搭建个人网站?
香港服务器选型指南:免备案配置与高效建站方案解析
如何在橙子建站中快速调整背景颜色?
bing浏览器学术搜索入口_bing学术文献检索地址
,怎么在广州志愿者网站注册?
Android实现代码画虚线边框背景效果
Edge浏览器提示“由你的组织管理”怎么解决_去除浏览器托管提示【修复】
WordPress 子目录安装中正确处理脚本路径的完整指南
Laravel如何使用API Resources格式化JSON响应_Laravel数据资源封装与格式化输出
Laravel如何实现多对多模型关联?(Eloquent教程)
Win11搜索不到蓝牙耳机怎么办 Win11蓝牙驱动更新修复【详解】
Laravel如何使用Gate和Policy进行权限控制_Laravel权限判定与策略规则配置
Win11应用商店下载慢怎么办 Win11更改DNS提速下载【修复】
谷歌Google入口永久地址_Google搜索引擎官网首页永久入口
Laravel软删除怎么实现_Laravel Eloquent SoftDeletes功能使用教程
Laravel Fortify是什么,和Jetstream有什么关系
小米17系列还有一款新机?主打6.9英寸大直屏和旗舰级影像
如何快速上传自定义模板至建站之星?
Win11怎么开启自动HDR画质_Windows11显示设置HDR选项
Laravel如何实现多级无限分类_Laravel递归模型关联与树状数据输出【方法】
C++用Dijkstra(迪杰斯特拉)算法求最短路径
如何在 Go 中优雅地映射具有动态字段的 JSON 对象到结构体
Laravel API资源(Resource)怎么用_格式化Laravel API响应的最佳实践
北京专业网站制作设计师招聘,北京白云观官方网站?
ChatGPT常用指令模板大全 新手快速上手的万能Prompt合集
黑客入侵网站服务器的常见手法有哪些?
EditPlus中的正则表达式实战(5)
JS去除重复并统计数量的实现方法
简历在线制作网站免费版,如何创建个人简历?
常州企业网站制作公司,全国继续教育网怎么登录?
网站页面设计需要考虑到这些问题
如何在云虚拟主机上快速搭建个人网站?
韩国代理服务器如何选?解析IP设置技巧与跨境访问优化指南
如何在建站之星绑定自定义域名?
Laravel如何连接多个数据库_Laravel多数据库连接配置与切换教程
Laravel中的Facade(门面)到底是什么原理
桂林网站制作公司有哪些,桂林马拉松怎么报名?
详解Nginx + Tomcat 反向代理 负载均衡 集群 部署指南

