Mantle是什么?
Mantle makes it easy to write a simple model layer for your Cocoa or Cocoa Touch application. --Mantle是構(gòu)建Cocoa或者Cocoa Touch應(yīng)用程序的Model層的框架。
Mantle地址:GitHub - Mantle/Mantle: Model framework for Cocoa and Cocoa Touch
Xtrace是什么?
Xtrace是ios中強(qiáng)大的調(diào)試的庫(kù),能詳細(xì)打印出一個(gè)某個(gè)方法被調(diào)用的堆棧,方便調(diào)試時(shí)定位問題。
Xtrace地址:GitHub - johnno1962/Xtrace: Trace Objective-C method calls by class or instance
OK~提供了以上信息之后,正式開始我們的主題。不同于其他大神模式,一上來(lái)就分析源碼+總結(jié),今天我們借助與Xtrace工具來(lái)一步步教你讀懂Mantle源碼。
開始
在我們的Mantle案例中需要添加Xtrace的支持,將Xtrace.h和Xtrace.mm兩個(gè)文件添加到我們的項(xiàng)目中,并在AppDelegate.m中添加Xtrace的引用和配置相關(guān)項(xiàng)。

大家了解過Mantle使用的人大致應(yīng)該了解MTLModel類、MTLJSONAdapter類以及MTLJSONSerializing協(xié)議是整個(gè)Mantle的核心,所以我們?cè)谏厦娴腦Trace配置項(xiàng)中針對(duì)MTLModel的子類GHIssueMantle(實(shí)現(xiàn)MTLJSONSerializing協(xié)議)和MTLJSONAdapter類進(jìn)行堆棧跟蹤。以下是我們解析過程中經(jīng)常使用的代碼:

我們來(lái)看一下XTrace跟蹤到的堆棧情況:

大家可以看到從入口代碼到Mantle內(nèi)部的執(zhí)行過程就非常的清晰了,整個(gè)過程從JSON串轉(zhuǎn)換到Model所涉及到的方法的執(zhí)行過程是按順序進(jìn)行的,所以接下來(lái)我們主要針對(duì)這些這些方法進(jìn)行一個(gè)解讀。
方法一:modelOfClass:fromJSONDictionary:error:

這個(gè)方法通過initWithModelClass方法初始化MTLJSONAdapter實(shí)例,之后調(diào)用modelFromJSONDictionary: error:將JSON字典轉(zhuǎn)化成Model;
方法二:initWithModelClass:

這個(gè)方法會(huì)主要用來(lái)初始化變量,并返回MTLJSONAdapter實(shí)例。先是調(diào)用了JSONKeyPathsByPropertyKey來(lái)指定對(duì)象的屬性如何映射到不同的key path,并將結(jié)果保存。再是調(diào)用propertyKeys方法獲取model中所有屬性(除了存儲(chǔ)類型為MTLPropertyStorageNone)。接著遍歷_JSONKeyPathsByPropertyKey變量,先判斷是不是包含在propertyKeys集合中,如果未包含直接return掉;如果包含則獲取對(duì)應(yīng)屬性keyPath,這里keyPath有兩種形式,一種字符串的形式、一種數(shù)組的形式,所以這里做了一下判斷。然后調(diào)用了valueTransformersForModelClass方法來(lái)獲取model中每個(gè)屬性對(duì)應(yīng)的值轉(zhuǎn)換器的字典。最后初始化了JSONAdaptersByModelClass變量并返回實(shí)例。
方法三:JSONKeyPathsByPropertyKey

這是子類Model遵守MTLJSONSerializing協(xié)議必須實(shí)現(xiàn)的方法,主要指定對(duì)象的屬性如何映射到不同的key path。
方法四:propertyKeys

這個(gè)方法主要獲取獲取model中所有屬性(除了存儲(chǔ)類型為MTLPropertyStorageNone)。這里通過關(guān)聯(lián)對(duì)象形式來(lái)獲取緩存,如果緩存為Nil,則調(diào)用enumeratePropertiesUsingBlock一次遍歷所有屬性,通過調(diào)用storageBehaviorForPropertyWithKey獲取每個(gè)屬性的存儲(chǔ)行為,并將存儲(chǔ)行為不是MTLPropertyStorageNone的結(jié)果加入到集合中,最后結(jié)果關(guān)聯(lián)到當(dāng)前對(duì)象并返回。
方法五:enumeratePropertiesUsingBlock

這個(gè)方法主要來(lái)循環(huán)獲取model及其父類(非MTLModel)的所有屬性,并執(zhí)行block。這里運(yùn)用到了runtime函數(shù)class_copyPropertyList來(lái)獲取屬性列表。
方法六:storageBehaviorForPropertyWithKey

這個(gè)方法主要用來(lái)獲取指定屬性的存儲(chǔ)行為。先是通過runtime獲取屬性,并通過mtl_copyPropertyAttributes獲取屬性內(nèi)部結(jié)構(gòu)。之后的判斷邏輯相對(duì)比較復(fù)雜,同時(shí)也說(shuō)明作者考慮比較周到。從這個(gè)邏輯判斷我們可以知道,Mantle實(shí)際將存儲(chǔ)類型分類為兩種(可存儲(chǔ)和不可存儲(chǔ)),而不可存儲(chǔ)的屬性是沒有變量空間ivar,readonly屬性往往會(huì)是這種情況。這里內(nèi)部出現(xiàn)嵌套調(diào)用的情況,主要是考慮到了一個(gè)被覆蓋的readonly屬性其父類可能存在變量空間。
方法七:valueTransformersForModelClass


這個(gè)方法用來(lái)獲取model中屬性對(duì)應(yīng)的值轉(zhuǎn)換器集合。先去判斷了是否實(shí)現(xiàn)"屬性名稱"+"JSONTransformer"方法,如果實(shí)現(xiàn)進(jìn)行動(dòng)態(tài)調(diào)用并返回。如果未實(shí)現(xiàn)則判斷是否實(shí)現(xiàn)JSONTransformerForKey這個(gè)方法,如果實(shí)現(xiàn)則進(jìn)行調(diào)用并返回。如果未實(shí)現(xiàn),則對(duì)屬性結(jié)構(gòu)進(jìn)行判斷,返回合適的值轉(zhuǎn)換器。這里因?yàn)槲覀儗?shí)現(xiàn)了URLJSONTransformer這個(gè)方法,所以代用完URLJSONTransformer就直接返回了。
注意:第373行的函數(shù)調(diào)用方式可以在一個(gè)循環(huán)內(nèi)頻繁地調(diào)用一個(gè)特定的方法時(shí),通過這種方式可以提高程序的性能,免去了runtime方法查找的性能損失。
方法八:modelOfClass:fromJSONDictionary:error:



這個(gè)方法是將JSON轉(zhuǎn)換為Model最主要的方法,前面做的都是一些初始化工作。先是判斷當(dāng)前類是否實(shí)現(xiàn)了classForParsingJSONDictionary方法,這個(gè)方法在存在類族的情況下判斷具體哪個(gè)類來(lái)執(zhí)行轉(zhuǎn)換工作。接著遍歷model的所有屬性,通過kvc獲取每個(gè)屬性的值,并判斷相應(yīng)屬性是否存在值轉(zhuǎn)換器,若果存在,則對(duì)值進(jìn)行相應(yīng)轉(zhuǎn)換。最后將所有的值存儲(chǔ)在字典中,并調(diào)用modelWithDictionary: error通過kvc的方式驗(yàn)證并設(shè)置值。
方法九:modelWithDictionary: error

這個(gè)方法只是調(diào)用了initWithDictionary: error:自定義實(shí)現(xiàn),而我在這個(gè)方法只是簡(jiǎn)單的調(diào)用了MTLModel的initWithDictionary: error:實(shí)現(xiàn)。
方法十:initWithDictionary: error:

這個(gè)方法通過kvc的方式初始化model實(shí)例。先是遍歷了字典,獲取值并驗(yàn)證是否為NSNull.null類型,接著通過KVC的方式為model實(shí)例驗(yàn)證并設(shè)置值。注意:XTrace跟蹤不到靜態(tài)函數(shù)。所以這里申明的MTLValidateAndSetValue函數(shù)并未跟蹤到。
附上案例地址:GitHub - qsw1214/MantleDemo: A MantleDemo for learing
如果理解有錯(cuò)誤的地方,歡迎大家可以指出來(lái),一起探討。