Mantle 的類層次關(guān)系圖:
CustomModel 類澳叉,就是我們自定義的 Model,在 MVC 或者 MVVM 開發(fā)模式上必不可少的 Model 類沐悦,如:User, Note, Tage 等成洗。
-
基類MTLModel 提供了一些默認(rèn)的行為來處理對象的初始化和歸檔操作,同時可以獲取到對象所有屬性的鍵值集合藏否。主要是兩件事:
- 將dictionary 鍵值對瓶殃,映射成該類的所有屬性(名字為 key)的值;
- 將類的所有屬性作為 key副签,屬性值作為 value遥椿,轉(zhuǎn)換成dictionary;
MTLJSONSerializing 該協(xié)議可以讓用戶自定義哪些JSON字段基矮,映射成 CustomModel 的哪些屬性,名稱不一致的時候修壕,極為有用愈捅。
- MTLJSONAdaptor 起到一個適配器的作用,將 JSON Dict 進行驗證慈鸠,將其鍵和值蓝谨,通過 CustomModel 上的屬性transformer,將 JSON的 value 轉(zhuǎn)換成定義的需要的類型 value, 比如將時間戳 long 類型青团,轉(zhuǎn)換成 NSDate 類型譬巫,構(gòu)建一個 Dictionary,傳入給 MTLModel構(gòu)造方法督笆,完成 JSON 到 model 的轉(zhuǎn)換芦昔;同理,通過 model 類型的 dictionary, 通過 model 反向 transformer 將 model 屬性值娃肿,轉(zhuǎn)換成 JSON value后咕缎,構(gòu)建一個 JSON Dict。這個 JSON Dict 就是我們需要通過網(wǎng)絡(luò)發(fā)送給服務(wù)器的結(jié)構(gòu)料扰。
可以看到 Mantle 核心的內(nèi)容就是兩個類凭豪,我們主要分析這兩個類的一些核心方法。
先分析一下MTLModel 類晒杈,從最上層的函數(shù)開始嫂伞,一步一步深入分析:
MTLModel 通過這個初始化方法,從 dictionary拯钻,初始化一個 Model 對象帖努。將 dictionary 上的 key 與 value,通過 KVC 機制粪般,將 model 的屬性進行初始化拼余,遍歷整個 dictionary 都沒有發(fā)生錯誤時,則返回初始化對象亩歹。如果在 KVC 驗證過程中發(fā)生錯誤姿搜,則直接返回 nil,終止循環(huán)捆憎。在 MTLValidateAndSetValue
方法的第四個參數(shù),forceUpdate梭纹,傳入 YES躲惰,表示驗證通過后,強制設(shè)置值变抽,方法體的內(nèi)容如下:
如此就完成了 Dictionary 轉(zhuǎn)換成 Model 類础拨。
MTLModel 定義了一個 dictionaryValue 屬性氮块,并定義了 getter 方法。目的是完成 model 轉(zhuǎn)換為 dictionary:
將 transitory 屬性鍵與 permanent 屬性鍵相加诡宗,然后通過 KVC 獲取這些鍵的值滔蝉,組成一個 dictionary 作為返回值。
從關(guān)聯(lián)對象上獲取這些塔沃,如果為 nil蝠引,調(diào)用 generateAndCacheStorageBehaviors
方法生成。
這個方法蛀柴,分四步:
遍歷所有屬性螃概。
根據(jù)屬性的存儲類型,進行分類鸽疾。
分好類吊洼,分別添加 至對應(yīng)的集合,并且設(shè)置關(guān)聯(lián)對象制肮,使用copy 模式冒窍。
循環(huán)遍歷屬性,調(diào)用 block 進行設(shè)置豺鼻。
第1步遍歷所有屬性的方法為:propertyKeys
判斷其拷貝行為综液,過濾掉不做存儲的屬性,需要注意的是它同時會對 hash 拘领、 superclass 意乓、 description 、 debugDescription 這四個屬性進行判斷约素,在 NSObject 內(nèi)這四個都是 readonly 的届良,如果你不去將它設(shè)為 readwrite 的話,它們是不做存儲的圣猎。
這里再講一下Mantle的存儲行為士葫。
MTLModel用了一個枚舉 MTLPropertyStorage 來標(biāo)記一個屬性的拷貝行為,分為三類:
MTLPropertyStorageNone :屬性不做任何存儲送悔,在 MTLModel里判斷不存儲的條件是1.沒有該屬性慢显,自然不用存儲 2.該屬性沒有使用 @dynamic 指令,但是沒有成員變量欠啤,并且沒有對應(yīng)的setter和getter方法 3. MTLModel 類中屬性是只讀荚藻,且沒有成員變量。
MTLPropertyStorageTransitory :屬性只做暫時性的存儲洁段,在官方解釋里看到一句話
It may disappear at any time
应狱,感覺指的是弱引用的屬性,但是在 MTLModel 里并沒有看到返回MTLPropertyStorageTransitory
的處理祠丝。MTLPropertyStoragePermanent 疾呻,屬性做永久存儲除嘹, MTLModel 里判斷只要不是 MTLPropertyStorageNone 就是 MTLPropertyStoragePermanent,需要做暫時存儲的岸蜗,就需要在子類里重寫了尉咕。
第4步循環(huán)遍歷所有屬性(包括本類及其父類)的方法為:
這里遍歷類及其父類,一直到 MTLModel 根類為止璃岳。獲取它的屬性年缎,如果沒有屬性值繼續(xù)父類,如果有矾睦,使用 onExit 語法(相當(dāng)于 Swift 上 defer 關(guān)鍵字)晦款,在作用域結(jié)束的時候,釋放掉 properties枚冗。
onExit 定義:
attribute((cleanup(mtl_executeCleanupBlock)) 在作用域離開的時候缓溅,進行清理工作。會調(diào)用mtl_executeCleanupBlock函數(shù)赁温,并將 block 的地址傳入坛怪,在函數(shù)體內(nèi)直接調(diào)用這個 block。
驗證屬性的存儲類型方法:
這里面用到一個屬性結(jié)構(gòu)股囊,方便管理一個屬性的描述內(nèi)容袜匿。如果一個屬性沒有g(shù)etter, setter, 并且沒有對應(yīng)的變量稚疹,則返回.none 類型居灯。如果是只讀且沒有對應(yīng)的變量,到了根類 MTLModel 返回 none内狗,否則驗證父類的關(guān)于這個鍵值的存儲類型怪嫌。其他返回的都是 permanent 類型。
到目前為止柳沙,MTLModel 將 model轉(zhuǎn) dictionary 就完成了岩灭。
接下來分析一下 MTLJSONAdapter 類和 MTLJSONSerializing 協(xié)議。
MTLJSONSerializing:
第一個方法赂鲤,是必須實現(xiàn)的方法噪径,它是用于將屬性和 JSON 的解析路徑做關(guān)聯(lián),它不僅僅可以用于給屬性起“別名”数初,還可以用于多級解析和多層嵌套找爱,用官方例子來舉例:
+ (NSDictionary *)JSONKeyPathsByPropertyKey {
return @{
@"name": @"POI.name",
@"point": @[ @"latitude", @"longitude" ],
@"starred": @"starred"
};
}
在映射過程中 starred 與 JSONDictionary[@"starred"] 做映射,
name 與 JSONDictionary[@"POI"][@"name"] 做映射泡孩,
point 則等同于以下這個 dictionary
@{
@"latitude": JSONDictionary[@"latitude"],
@"longitude": JSONDictionary[@"longitude"]
}
第二個方法缴允,是 model 實現(xiàn)的 +< key >JSONTransformer
方法,如果有則使用它進行轉(zhuǎn)換的值。
第三個方法是用于類簇练般。
@interface XYMessage : MTLModel
@end
@interface XYTextMessage: XYMessage
@property (readonly, nonatomic, copy) NSString *body;
@end
@interface XYPictureMessage : XYMessage
@property (readonly, nonatomic, strong) NSURL *imageURL;
@end
@implementation XYMessage
+ (Class)classForParsingJSONDictionary:(NSDictionary *)JSONDictionary {
if (JSONDictionary[@"image_url"] != nil) {
return XYPictureMessage.class;
}
if (JSONDictionary[@"body"] != nil) {
return XYTextMessage.class;
}
NSAssert(NO, @"No matching class for the JSON dictionary '%@'.", JSONDictionary);
return self;
}
@end
MTLJSONAdapter
1. JSON data -> using Model class -> model object
第一個方法的核心內(nèi)容是 modelFromJSONDictionary: error:
方法。數(shù)組的類型锈候,需要循環(huán)遍歷數(shù)組內(nèi)的 JSONDictionary 數(shù)據(jù)薄料,調(diào)用第一個方法。
從 model class 上獲取所有的屬性泵琳,參考上面的MTLModel propertyKeys方法;
遍歷這些屬性摄职,并且根據(jù)屬性作為 key 獲取 JSON 上的 value;如果沒有值获列,不用轉(zhuǎn)換谷市,直接 continue,繼續(xù)下一個循環(huán)击孩;
獲取屬性的轉(zhuǎn)換器 valueTransformer迫悠,用戶從 JSON value 轉(zhuǎn)換到自定義的值,比如 long 類型的時間戳巩梢,轉(zhuǎn)換到 NSDate 類型创泄,也可以從 JSON的字符串,映射到 Objective-C 枚舉類型括蝠;
將 key 和轉(zhuǎn)換后的 value鞠抑,構(gòu)建一個鍵值對,保存起來忌警,最終形成一個 dicionaryValue;
使用 MTLModel 的Dictionary 轉(zhuǎn)換成 Model 對象的方法搁拙;
使用 KVC 驗證轉(zhuǎn)換后的 model 是否有效,無效返回 nil法绵,有效則返回 model 對象箕速。
通過以上六步,將 JSON data礼烈,通過 Model class弧满,轉(zhuǎn)換成 model object。
2. model object -> using Model class -> JSON data
第一個方法的核心內(nèi)容是 JSONDictionaryFromModel: error:
方法此熬,數(shù)組類型的庭呜,需要循環(huán)遍歷數(shù)組中 model 對象,將其出入第一個方法犀忱。
核對 model class 類型募谎;
獲取需要轉(zhuǎn)換的 keys。 model 實現(xiàn)的 MTLJSONSerializing 協(xié)議中的
JSONKeyPathsByPropertyKey
方法阴汇,并將這些 keys 對應(yīng) model class 的 values数冬,構(gòu)建 dictionaryValue;遍歷dictionaryValue, 獲取 JSON 上的 keyPaths;
獲取 model 定義的 value transformer拐纱;
是否允許反向轉(zhuǎn)換铜异;
如果定義這個
reverseTransformedValue: success: error:
方法,轉(zhuǎn)換錯誤處理秸架,更加安全揍庄;如果沒有,調(diào)用
reverseTransformedValue
換成 value东抹。如果返回 nil蚂子,則設(shè)置為 NSNull.null;創(chuàng)建一個根據(jù) keyPath缭黔,分割鍵值食茎,填充對象的 createComponents block;
如果 JSONKeyPaths是 字符串類型馏谨,則將轉(zhuǎn)換后的 value 對象别渔,key 為 JSONKeyPaths, 構(gòu)建鍵值對,并保存起來田巴;
如果 JSONKeyPaths 是數(shù)組钠糊,遍歷這個路徑, 對于每個 value, 調(diào)用第八步的 createComponents block,并且執(zhí)行第九步的操作壹哺。
經(jīng)過以上十步抄伍,完成了model object 通過 model class,轉(zhuǎn)換成 JSON dictionary data管宵,可以將其發(fā)送給服務(wù)器端截珍。
以上,就是 Mantle 實現(xiàn)的核心內(nèi)容箩朴。