教你用Xtrace讀懂Mantle原始碼

千客發表於2016-04-04

Mantle是什麼?

Mantle makes it easy to write a simple model layer for your Cocoa or Cocoa Touch application. –Mantle是構建Cocoa或者Cocoa Touch應用程式的Model層的框架。

Mantle地址:GitHub – Mantle/Mantle: Model framework for Cocoa and Cocoa Touch

Xtrace是什麼?

Xtrace是ios中強大的除錯的庫,能詳細列印出一個某個方法被呼叫的堆疊,方便除錯時定位問題。

Xtrace地址:GitHub – johnno1962/Xtrace: Trace Objective-C method calls by class or instance

OK~提供了以上資訊之後,正式開始我們的主題。不同於其他大神模式,一上來就分析原始碼+總結,今天我們藉助與Xtrace工具來一步步教你讀懂Mantle原始碼。

開始

在我們的Mantle案例中需要新增Xtrace的支援,將Xtrace.h和Xtrace.mm兩個檔案新增到我們的專案中,並在AppDelegate.m中新增Xtrace的引用和配置相關項。

 
Xtrace相關配置項

大家瞭解過Mantle使用的人大致應該瞭解MTLModel類、MTLJSONAdapter類以及MTLJSONSerializing協議是整個Mantle的核心,所以我們在上面的XTrace配置項中針對MTLModel的子類GHIssueMantle(實現MTLJSONSerializing協議)和MTLJSONAdapter類進行堆疊跟蹤。以下是我們解析過程中經常使用的程式碼:

 
入口程式碼

我們來看一下XTrace跟蹤到的堆疊情況:

 
XTrace呼叫堆疊

大家可以看到從入口程式碼到Mantle內部的執行過程就非常的清晰了,整個過程從JSON串轉換到Model所涉及到的方法的執行過程是按順序進行的,所以接下來我們主要針對這些這些方法進行一個解讀。

方法一:modelOfClass:fromJSONDictionary:error:

 
modelOfClass:fromJSONDictionary:error:

這個方法通過initWithModelClass方法初始化MTLJSONAdapter例項,之後呼叫modelFromJSONDictionary: error:將JSON字典轉化成Model;

方法二:initWithModelClass:

 
initWithModelClass:

這個方法會主要用來初始化變數,並返回MTLJSONAdapter例項。先是呼叫了JSONKeyPathsByPropertyKey來指定物件的屬性如何對映到不同的key path,並將結果儲存。再是呼叫propertyKeys方法獲取model中所有屬性(除了儲存型別為MTLPropertyStorageNone)。接著遍歷_JSONKeyPathsByPropertyKey變數,先判斷是不是包含在propertyKeys集合中,如果未包含直接return掉;如果包含則獲取對應屬性keyPath,這裡keyPath有兩種形式,一種字串的形式、一種陣列的形式,所以這裡做了一下判斷。然後呼叫了valueTransformersForModelClass方法來獲取model中每個屬性對應的值轉換器的字典。最後初始化了JSONAdaptersByModelClass變數並返回例項。

方法三:JSONKeyPathsByPropertyKey

 
JSONKeyPathsByPropertyKey

這是子類Model遵守MTLJSONSerializing協議必須實現的方法,主要指定物件的屬性如何對映到不同的key path。

方法四:propertyKeys

 
propertyKeys

這個方法主要獲取獲取model中所有屬性(除了儲存型別為MTLPropertyStorageNone)。這裡通過關聯物件形式來獲取快取,如果快取為Nil,則呼叫enumeratePropertiesUsingBlock一次遍歷所有屬性,通過呼叫storageBehaviorForPropertyWithKey獲取每個屬性的儲存行為,並將儲存行為不是MTLPropertyStorageNone的結果加入到集合中,最後結果關聯到當前物件並返回。

方法五:enumeratePropertiesUsingBlock

 
enumeratePropertiesUsingBlock

這個方法主要來迴圈獲取model及其父類(非MTLModel)的所有屬性,並執行block。這裡運用到了runtime函式class_copyPropertyList來獲取屬性列表。

方法六:storageBehaviorForPropertyWithKey

 
storageBehaviorForPropertyWithKey

這個方法主要用來獲取指定屬性的儲存行為。先是通過runtime獲取屬性,並通過mtl_copyPropertyAttributes獲取屬性內部結構。之後的判斷邏輯相對比較複雜,同時也說明作者考慮比較周到。從這個邏輯判斷我們可以知道,Mantle實際將儲存型別分類為兩種(可儲存和不可儲存),而不可儲存的屬性是沒有變數空間ivar,readonly屬性往往會是這種情況。這裡內部出現巢狀呼叫的情況,主要是考慮到了一個被覆蓋的readonly屬性其父類可能存在變數空間。

方法七:valueTransformersForModelClass

 
 
valueTransformersForModelClass

這個方法用來獲取model中屬性對應的值轉換器集合。先去判斷了是否實現”屬性名稱”+”JSONTransformer”方法,如果實現進行動態呼叫並返回。如果未實現則判斷是否實現JSONTransformerForKey這個方法,如果實現則進行呼叫並返回。如果未實現,則對屬性結構進行判斷,返回合適的值轉換器。這裡因為我們實現了URLJSONTransformer這個方法,所以代用完URLJSONTransformer就直接返回了。

注意:第373行的函式呼叫方式可以在一個迴圈內頻繁地呼叫一個特定的方法時,通過這種方式可以提高程式的效能,免去了runtime方法查詢的效能損失。

方法八:modelOfClass:fromJSONDictionary:error:

 
 
 
[MTLJSONAdapter modelOfClass:fromJSONDictionary:error:

這個方法是將JSON轉換為Model最主要的方法,前面做的都是一些初始化工作。先是判斷當前類是否實現了classForParsingJSONDictionary方法,這個方法在存在類族的情況下判斷具體哪個類來執行轉換工作。接著遍歷model的所有屬性,通過kvc獲取每個屬性的值,並判斷相應屬性是否存在值轉換器,若果存在,則對值進行相應轉換。最後將所有的值儲存在字典中,並呼叫modelWithDictionary: error通過kvc的方式驗證並設定值。

方法九:modelWithDictionary: error

 
modelWithDictionary: error

這個方法只是呼叫了initWithDictionary: error:自定義實現,而我在這個方法只是簡單的呼叫了MTLModel的initWithDictionary: error:實現。

方法十:initWithDictionary: error:

 
initWithDictionary: error:

這個方法通過kvc的方式初始化model例項。先是遍歷了字典,獲取值並驗證是否為NSNull.null型別,接著通過KVC的方式為model例項驗證並設定值。注意:XTrace跟蹤不到靜態函式。所以這裡申明的MTLValidateAndSetValue函式並未跟蹤到。

附上案例地址:GitHub – qsw1214/MantleDemo: A MantleDemo for learing

如果理解有錯誤的地方,歡迎大家可以指出來,一起探討。

打賞支援我寫出更多好文章,謝謝!

打賞作者

打賞支援我寫出更多好文章,謝謝!

任選一種支付方式

教你用Xtrace讀懂Mantle原始碼 教你用Xtrace讀懂Mantle原始碼

相關文章