iOS中創(chuàng)建Model的最佳實(shí)踐記錄
前言
作為一個(gè)優(yōu)秀的程序員,或者想成為優(yōu)秀的程序員,最基本的你得有MVC編程思想,那么你就要對(duì)JSON獲取的數(shù)據(jù)建Model,將service和controller層都分離,從而做到低耦合。現(xiàn)在有很多利用runtime能快速的將json數(shù)據(jù)轉(zhuǎn)為一個(gè)Model。但是我在做項(xiàng)目的時(shí)候,發(fā)現(xiàn)創(chuàng)建Model(特別是屬性特多的)寫屬性代碼很浪費(fèi)時(shí)間,降低了編程效率。后來(lái)我自己就寫了個(gè)好玩的能省去時(shí)間創(chuàng)建Model的一個(gè)方法,下面話不多說(shuō)了,來(lái)一起看看詳細(xì)的介紹吧
Immutable Model
我們以UserModle為例,我們可以像這樣創(chuàng)建:
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() } }
用的時(shí)候可以像這樣:
let userModel = UserModel(userId: 1) user.email = "335050309@qq.com" user.name = "roy" user.age = 27 user.address = "上海市楊浦區(qū)"
這樣創(chuàng)建一個(gè)User對(duì)象好處是彈性很大,我可以隨意選擇設(shè)定某個(gè)property的值,但是背后同樣帶有很大的缺點(diǎn),就是這個(gè)Model變得異常開(kāi)放,不安分,這種Model我們一般叫Mutable Model。有的時(shí)候我們需要Mutable Model,但大部分的時(shí)候出于數(shù)據(jù)安全和解耦考慮我們不希望創(chuàng)建的property在外部可以隨意改變,在初始化后不可變的Model叫做Immutable Model,在開(kāi)發(fā)中我的建議盡量使用Immutable Model。我們通過(guò)把property設(shè)置成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
當(dāng)我們把property設(shè)置成readonly后,我們只能在init的時(shí)候賦值,這個(gè)時(shí)候就變成這樣:
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 } }
使用的時(shí)候就變成這樣:
let user = User.init(userId: 1, name: "335050309@qq.com", email: "roy", age: 27, address: "上海市楊浦區(qū)")
這樣創(chuàng)建Model安全可靠,大多數(shù)時(shí)候是有效的,但是也有一些缺點(diǎn):
- 如果property很多,init方法就有很多形參,然后變得又臭又長(zhǎng)。
- 有的時(shí)候我們只需要Model的某些property,這樣我們可能為各個(gè)不同的需求寫不同的init方法,最終讓UserModel變得很龐大。
Initializer taking dictionary
初始化的時(shí)候注入一個(gè)字典,就是下面的樣子:
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 } }
很顯然這解決上一種第一個(gè)缺點(diǎn),但是還是有一個(gè)不足之處:
- 如果字典沒(méi)有某個(gè)屬性對(duì)應(yīng)的key的時(shí)候會(huì)崩潰,編譯器并不能幫助我們排查這種運(yùn)行時(shí)的崩潰。
- 不能很好的滿足某些時(shí)候只需要Model的某些property的需求。
Mutable subclass
我們看看Improving Immutable Object Initialization in Objective-C關(guān)于這個(gè)是怎么描述的
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.
大致意思是創(chuàng)建一個(gè)可變子類,它將所有屬性重新定義為readwrite。這種方式的主要缺點(diǎn)是我們最終得到兩倍的類。而且,可變子類通常僅作為初始化和修改其不可變版本的方式存在。偶然使用可變子類可能會(huì)導(dǎo)致許多錯(cuò)誤。例如,在設(shè)置屬性時(shí)會(huì)出現(xiàn)心理負(fù)擔(dān)。我們必須始終檢查是否存在可變子類。
還有一點(diǎn)這種方式只能在Objective-C中使用。
Builder pattern
Builder pattern 模式需要我們使用一個(gè)Builder來(lái)創(chuàng)建目標(biāo)對(duì)象,目標(biāo)對(duì)象的property依舊是readonly,但是Builder的對(duì)應(yīng)property卻可以選擇為readwrite。依舊用UserModel為例,我們需要為其進(jìn)行適當(dāng)?shù)母脑?,改造之后?br />
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 } }
之后是對(duì)應(yīng)的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 = "上海市楊浦區(qū)" return builder }
這種方式雖然我們需要為Model再創(chuàng)建一個(gè)Builder,略顯啰嗦和復(fù)雜,但是當(dāng)property較多,對(duì)Model的需求又比較復(fù)雜的時(shí)候這又確實(shí)是一種值得推薦的方式。
以上全是Swift的代碼實(shí)現(xiàn),下面我再貼上對(duì)應(yīng)的OC代碼
#import <Foundation/Foundation.h> @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(本地下載)
參考文章:
Improving Immutable Object Initialization in Objective-C
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
iOS中利用CAGradientLayer繪制漸變色的方法實(shí)例
有時(shí)候iOS開(kāi)發(fā)中需要使用到漸變色,來(lái)給圖片或者view蓋上一層,使其顯示效果更好,所以這篇文章主要給大家介紹了關(guān)于iOS中利用CAGradientLayer繪制漸變色的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下。2017-11-11IOS 避免self循環(huán)引用的方法的實(shí)例詳解
這篇文章主要介紹了IOS 避免self循環(huán)引用的方法的實(shí)例詳解的相關(guān)資料,希望通過(guò)本文能幫助到大家,需要的朋友可以參考下2017-09-09iOS App中調(diào)用相冊(cè)中圖片及獲取最近的一張圖片的方法
這篇文章主要介紹了iOS App中調(diào)用相冊(cè)中圖片及獲取最近的一張圖片的方法,示例代碼為傳統(tǒng)的Objective-C語(yǔ)言,需要的朋友可以參考下2016-03-03iOS中從網(wǎng)絡(luò)獲取數(shù)據(jù)的幾種方法的比較
IOS中獲取網(wǎng)絡(luò)數(shù)據(jù)一般有三種:1、NSURLCondition(已過(guò)時(shí)) 2、NSURLSession 3、三方庫(kù)AFNetWorking。下面通過(guò)本文給大家比較這三種方法的區(qū)別對(duì)比2017-11-11

iOS 解決UICollectionView 計(jì)算 Cell 大小的問(wèn)題

iOS應(yīng)用開(kāi)發(fā)中使用設(shè)計(jì)模式中的觀察者模式的實(shí)例

詳解iOS App開(kāi)發(fā)中Cookie的管理方法