内容简介:我们以UserModle为例,我们可以像这样创建:用的时候可以像这样:这样创建一个User对象好处是弹性很大,我可以随意选择设定某个property的值,但是背后同样带有很大的缺点,就是这个Model变得异常开放,不安分,这种Model我们一般叫Mutable Model。有的时候我们需要Mutable Model,但大部分的时候出于数据安全和解耦考虑我们不希望创建的property在外部可以随意改变,在初始化后不可变的Model叫做Immutable Model,在开发中我的建议尽量使用Immutabl
Immutable Model
我们以UserModle为例,我们可以像这样创建:
public class UserModel: NSObject { public var userId: NSNumber public var name: String? public var email: String? public var age: Int? public var address: String? init(userId: NSNumber) { self.userId = userId super.init() } }
用的时候可以像这样:
let userModel = UserModel(userId: 1) user.email = "335050309@qq.com" user.name = "roy" user.age = 27 user.address = "上海市杨浦区"
这样创建一个User对象好处是弹性很大,我可以随意选择设定某个property的值,但是背后同样带有很大的缺点,就是这个Model变得异常开放,不安分,这种Model我们一般叫Mutable Model。有的时候我们需要Mutable Model,但大部分的时候出于数据安全和解耦考虑我们不希望创建的property在外部可以随意改变,在初始化后不可变的Model叫做Immutable Model,在开发中我的建议尽量使用Immutable Model。我们通过把property设置成readonly,在Swift可以用let或者private(set)。也就是这样:
public class UserModel: NSObject { public let userId: NSNumber public private(set) var name: String? public private(set) var email: String? public private(set) var age: Int? public private(set) var address: String? }
那么怎么写初始化方法呢?
Initializer mapping arguments to properties
当我们把property设置成readonly后,我们只能在init的时候赋值,这个时候就变成这样:
public class User: NSObject { public var userId: NSNumber public var name: String? public var email: String? public var age: Int? public var address: String? init(userId: NSNumber, name: String?, email: String, age: Int, address: String) { self.userId = userId super.init() self.name = name self.email = email self.age = age self.address = address } }
使用的时候就变成这样:
let user = User.init(userId: 1, name: "335050309@qq.com", email: "roy", age: 27, address: "上海市杨浦区")
这样创建Model安全可靠,大多数时候是有效的,但是也有一些缺点:
-
如果property很多,init方法就有很多形参,然后变得又臭又长。
-
有的时候我们只需要Model的某些property,这样我们可能为各个不同的需求写不同的init方法,最终让UserModel变得很庞大。
Initializer taking dictionary
初始化的时候注入一个字典,就是下面的样子:
public class UserModel: NSObject { public let userId: NSNumber public private(set) var name: String? public private(set) var email: String? public private(set) var age: Int? public private(set) var address: String? init(dic: NSDictionary) { self.userId = (dic["userId"] as? NSNumber)! super.init() self.name = dic["name"] as? String self.email = dic["email"] as? String self.age = dic["age"] as? Int self.address = dic["address"] as? String } }
很显然这解决上一种第一个缺点,但是还是有一个不足之处:
-
如果字典没有某个属性对应的key的时候会崩溃,编译器并不能帮助我们排查这种运行时的崩溃。
-
不能很好的满足某些时候只需要Model的某些property的需求。
Mutable subclass
我们看看 Improving Immutable Object Initialization in Objective-C 关于这个是怎么描述的
We end up unsatisfied and continue our quest for the best way to initialize immutable objects. Cocoa is a vast land, so we can – and should – steal some of the ideas used by Apple in its frameworks. We can create a mutable subclass of Reminder class which redefines all properties as readwrite:
@interface MutableReminder : Reminder <NSCopying, NSMutableCopying> @property (nonatomic, copy, readwrite) NSString *title; @property (nonatomic, strong, readwrite) NSDate *date; @property (nonatomic, assign, readwrite) BOOL showsAlert; @end
Apple uses this approach for example in NSParagraphStyle and NSMutableParagraphStyle. We move between mutable and immutable counterparts with -copy and -mutableCopy. The most common case matches our example: a base class is immutable and its subclass is mutable.
The main disadvantage of this way is that we end up with twice as many classes. What's more, mutable subclasses often exist only as a way to initialize and modify their immutable versions. Many bugs can be caused by using a mutable subclass by accident. For example, a mental burden shows in setting up properties. We have to always check if a mutable subclass exists, and if so use copy modifier instead of strong for the base class.
大致意思是创建一个可变子类,它将所有属性重新定义为readwrite。这种方式的主要缺点是我们最终得到两倍的类。而且,可变子类通常仅作为初始化和修改其不可变版本的方式存在。偶然使用可变子类可能会导致许多错误。例如,在设置属性时会出现心理负担。我们必须始终检查是否存在可变子类。
还有一点这种方式只能在Objective-C中使用。
Builder pattern
Builder pattern 模式需要我们使用一个Builder来创建目标对象,目标对象的property依旧是readonly,但是Builder的对应property却可以选择为readwrite。依旧用UserModel为例,我们需要为其进行适当的改造,改造之后:
typealias UserModelBuilderBlock = (UserModelBuilder) -> UserModelBuilder public class UserModel: NSObject{ public let userId: NSNumber public private(set) var name: String? public private(set) var email: String? public private(set) var age: Int? public private(set) var address: String? init(userId: NSNumber) { self.userId = userId super.init() } convenience init(userId: NSNumber ,with block: UserModelBuilderBlock){ let userModelBuilder = block(UserModelBuilder.init(userId: userId)) self.init(userId: userModelBuilder.userId) self.email = userModelBuilder.email self.name = userModelBuilder.name self.age = userModelBuilder.age self.address = userModelBuilder.address } }
之后是对应的Builder
class UserModelBuilder: NSObject { public let userId: NSNumber public var name: String? public var email: String? public var age: Int? public var address: String? init(userId: NSNumber) { self.userId = userId super.init() } }
然后可以像下面这样使用:
let userModle = UserModel(userId: 1) { (builder) -> UserModelBuilder in builder.email = "335050309@qq.com" builder.name = "roy" builder.age = 27 builder.address = "上海市杨浦区" return builder }
这种方式虽然我们需要为Model再创建一个Builder,略显啰嗦和复杂,但是当property较多,对Model的需求又比较复杂的时候这又确实是一种值得推荐的方式。
以上全是Swift的代码实现,下面我再贴上对应的OC代码
#import
@interface RUserModelBuilder : NSObject
@property (nonatomic, strong, readwrite, nonnull) NSNumber *userId;
@property (nonatomic, copy, readwrite, nullable) NSString *name;
@property (nonatomic, copy, readwrite, nullable) NSString *email;
@property (nonatomic, copy, readwrite, nullable) NSNumber *age;
@property (nonatomic, copy, readwrite, nullable) NSString *address;
@end
typedef RUserModelBuilder *__nonnull(^RUserModelBuilderBlock)(RUserModelBuilder *__nonnull userModelBuilder);
@interface RUserModel : NSObject
@property (nonatomic, strong, readonly, nonnull) NSNumber *userId;
@property (nonatomic, copy, readonly, nullable) NSString *name;
@property (nonatomic, copy, readonly, nullable) NSString *email;
@property (nonatomic, copy, readonly, nullable) NSNumber *age;
@property (nonatomic, copy, readonly, nullable) NSString *address;
+ (nonnull instancetype)buildWithBlock:(nonnull RUserModelBuilderBlock)builderBlock;
@end
#import "RUserModel.h" @implementation RUserModelBuilder @end @interface RUserModel () @property (nonatomic, strong, readwrite, nonnull) NSNumber *userId; @property (nonatomic, copy, readwrite, nullable) NSString *name; @property (nonatomic, copy, readwrite, nullable) NSString *email; @property (nonatomic, copy, readwrite, nullable) NSNumber *age; @property (nonatomic, copy, readwrite, nullable) NSString *address; @end @implementation RUserModel #pragma mark - NSCopying + (nonnull instancetype)buildWithBlock:(nonnull RUserModelBuilderBlock)builderBlock { RUserModelBuilder *userModelBuilder = builderBlock([[RUserModelBuilder alloc] init]); RUserModel *userModel = [[RUserModel alloc] init]; userModel.userId = userModelBuilder.userId; userModel.name = userModelBuilder.name; userModel.email = userModelBuilder.email; userModel.age = userModelBuilder.age; userModel.address = userModelBuilder.address; return userModel; } @end
demo地址 ImmutableModel
参考文章:
以上所述就是小编给大家介绍的《iOS创建Model的最佳实践》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- Django入门与实践-第2章:创建项目
- (全栈学习实践)三、创建php、添加扩展,其他Dockerfile编写
- iOS App创建证书,添加Appid,创建配置文件流程
- RabbitMQ集群创建
- 创建哈夫曼树
- RabbitMQ集群创建
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
付费:互联网知识经济的兴起
方军 / 机械工业出版社 / 2017-6-1 / CNY 59.00
关于互联网知识付费的首部作品 知识工作正在被重塑,知识经济正在开启互联网时代下半场 为你展现互联网知识经济全景大图,解读新物种的前世今生 内容简介 一个产业解读 三个分析工具 一组知识卡片 书是最早的知识载体,已有2000多年的付费历史,随着移动互联网的普及,新的知识经 济在今天爆发,知识的创造者和传播者从书后走到了书前,互联网知识经济正在拉开帷幕。知识的......一起来看看 《付费:互联网知识经济的兴起》 这本书的介绍吧!