Mantle 源碼解讀

Mantle 的類層次關(guān)系圖:

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 init方法

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 方法生成。

generateAndCacheStorageBehaviors

這個方法蛀柴,分四步:

  1. 遍歷所有屬性螃概。

  2. 根據(jù)屬性的存儲類型,進行分類鸽疾。

  3. 分好類吊洼,分別添加 至對應(yīng)的集合,并且設(shè)置關(guān)聯(lián)對象制肮,使用copy 模式冒窍。

  4. 循環(huán)遍歷屬性,調(diào)用 block 進行設(shè)置豺鼻。

第1步遍歷所有屬性的方法為:propertyKeys 判斷其拷貝行為综液,過濾掉不做存儲的屬性,需要注意的是它同時會對 hash 拘领、 superclass 意乓、 description 、 debugDescription 這四個屬性進行判斷约素,在 NSObject 內(nèi)這四個都是 readonly 的届良,如果你不去將它設(shè)為 readwrite 的話,它們是不做存儲的圣猎。

這里再講一下Mantle的存儲行為士葫。

MTLModel用了一個枚舉 MTLPropertyStorage 來標(biāo)記一個屬性的拷貝行為,分為三類:

  1. MTLPropertyStorageNone :屬性不做任何存儲送悔,在 MTLModel里判斷不存儲的條件是1.沒有該屬性慢显,自然不用存儲 2.該屬性沒有使用 @dynamic 指令,但是沒有成員變量欠啤,并且沒有對應(yīng)的setter和getter方法 3. MTLModel 類中屬性是只讀荚藻,且沒有成員變量。

  2. MTLPropertyStorageTransitory :屬性只做暫時性的存儲洁段,在官方解釋里看到一句話 It may disappear at any time 应狱,感覺指的是弱引用的屬性,但是在 MTLModel 里并沒有看到返回 MTLPropertyStorageTransitory 的處理祠丝。

  3. MTLPropertyStoragePermanent 疾呻,屬性做永久存儲除嘹, MTLModel 里判斷只要不是 MTLPropertyStorageNone 就是 MTLPropertyStoragePermanent,需要做暫時存儲的岸蜗,就需要在子類里重寫了尉咕。

遍歷所有屬性方法

第4步循環(huán)遍歷所有屬性(包括本類及其父類)的方法為:

循環(huán)遍歷屬性方法

這里遍歷類及其父類,一直到 MTLModel 根類為止璃岳。獲取它的屬性年缎,如果沒有屬性值繼續(xù)父類,如果有矾睦,使用 onExit 語法(相當(dāng)于 Swift 上 defer 關(guān)鍵字)晦款,在作用域結(jié)束的時候,釋放掉 properties枚冗。

onExit 定義:

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

MTLJSONSerializing協(xié)議方法

第一個方法赂鲤,是必須實現(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)用第一個方法。

  1. 從 model class 上獲取所有的屬性泵琳,參考上面的MTLModel propertyKeys方法;

  2. 遍歷這些屬性摄职,并且根據(jù)屬性作為 key 獲取 JSON 上的 value;如果沒有值获列,不用轉(zhuǎn)換谷市,直接 continue,繼續(xù)下一個循環(huán)击孩;

  3. 獲取屬性的轉(zhuǎn)換器 valueTransformer迫悠,用戶從 JSON value 轉(zhuǎn)換到自定義的值,比如 long 類型的時間戳巩梢,轉(zhuǎn)換到 NSDate 類型创泄,也可以從 JSON的字符串,映射到 Objective-C 枚舉類型括蝠;

  4. 將 key 和轉(zhuǎn)換后的 value鞠抑,構(gòu)建一個鍵值對,保存起來忌警,最終形成一個 dicionaryValue;

  5. 使用 MTLModel 的Dictionary 轉(zhuǎn)換成 Model 對象的方法搁拙;

  6. 使用 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 對象,將其出入第一個方法犀忱。

  1. 核對 model class 類型募谎;

  2. 獲取需要轉(zhuǎn)換的 keys。 model 實現(xiàn)的 MTLJSONSerializing 協(xié)議中的 JSONKeyPathsByPropertyKey 方法阴汇,并將這些 keys 對應(yīng) model class 的 values数冬,構(gòu)建 dictionaryValue;

  3. 遍歷dictionaryValue, 獲取 JSON 上的 keyPaths;

  4. 獲取 model 定義的 value transformer拐纱;

  5. 是否允許反向轉(zhuǎn)換铜异;

  6. 如果定義這個 reverseTransformedValue: success: error: 方法,轉(zhuǎn)換錯誤處理秸架,更加安全揍庄;

  7. 如果沒有,調(diào)用 reverseTransformedValue 換成 value东抹。如果返回 nil蚂子,則設(shè)置為 NSNull.null;

  8. 創(chuàng)建一個根據(jù) keyPath缭黔,分割鍵值食茎,填充對象的 createComponents block;

  9. 如果 JSONKeyPaths是 字符串類型馏谨,則將轉(zhuǎn)換后的 value 對象别渔,key 為 JSONKeyPaths, 構(gòu)建鍵值對,并保存起來田巴;

  10. 如果 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)容箩朴。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末岗喉,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子炸庞,更是在濱河造成了極大的恐慌钱床,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件埠居,死亡現(xiàn)場離奇詭異查牌,居然都是意外死亡,警方通過查閱死者的電腦和手機滥壕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進店門纸颜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人绎橘,你說我怎么就攤上這事胁孙。” “怎么了?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵涮较,是天一觀的道長稠鼻。 經(jīng)常有香客問我,道長狂票,這世上最難降的妖魔是什么枷餐? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮苫亦,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘怨咪。我一直安慰自己屋剑,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布诗眨。 她就那樣靜靜地躺著唉匾,像睡著了一般。 火紅的嫁衣襯著肌膚如雪匠楚。 梳的紋絲不亂的頭發(fā)上巍膘,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天,我揣著相機與錄音芋簿,去河邊找鬼峡懈。 笑死,一個胖子當(dāng)著我的面吹牛与斤,可吹牛的內(nèi)容都是我干的肪康。 我是一名探鬼主播,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼撩穿,長吁一口氣:“原來是場噩夢啊……” “哼磷支!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起食寡,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤雾狈,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后抵皱,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體善榛,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年叨叙,在試婚紗的時候發(fā)現(xiàn)自己被綠了锭弊。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡擂错,死狀恐怖味滞,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤剑鞍,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布昨凡,位于F島的核電站,受9級特大地震影響蚁署,放射性物質(zhì)發(fā)生泄漏便脊。R本人自食惡果不足惜光戈,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦梗夸、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春趟庄,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工江兢, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留邑贴,地道東北人。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓哥纫,卻偏偏與公主長得像苛秕,于是被迫代替她去往敵國和親透罢。 傳聞我的和親對象是個殘疾皇子违霞,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理季希,服務(wù)發(fā)現(xiàn)蔬咬,斷路器曹动,智...
    卡卡羅2017閱讀 134,600評論 18 139
  • 概述 ? iOS源碼解析—YYModel(YYClassInfo)分析了如何根據(jù)OC的Class對象構(gòu)建...
    egoCogito_panf閱讀 11,506評論 4 32
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法叹放,類相關(guān)的語法饰恕,內(nèi)部類的語法,繼承相關(guān)的語法井仰,異常的語法埋嵌,線程的語...
    子非魚_t_閱讀 31,587評論 18 399
  • 導(dǎo)語:YYModel庫是優(yōu)秀的模型轉(zhuǎn)換庫,可自動處理模型轉(zhuǎn)換(從JSON到Model 和 Model到JSON)的...
    南華coder閱讀 5,423評論 0 11
  • 浪矢雜貨店從售賣各種商品到煩惱咨詢俱恶,看似偶然其實是必然雹嗦,因為最開始大家都沒有太在意范舀,只是覺得這樣做很有意思,而且大...
    旭之宇楓閱讀 518評論 0 0