Category、load、initialize

根據runtime的消息發送機制,核心函數void objc_msgSend(void /* id self, SEL op, … */ )發送消息,先從當先類的方法列表中查找調用方法,如果沒有找到,則會向上尋找其父類中的方法列表。
對於category重寫了主類中的方法,
OBJCKaTeX parse error: Expected group after '_' at position 17: …CATEGORY_Preson_̲Test.cls = OBJC_CLASS$_Preson

定義_class_t類型的OBJC_CLASS_KaTeX parse error: Expected group after '_' at position 20: …son結構體,最後將_OBJC_̲CATEGORY_PresonKaTeX parse error: Expected group after '_' at position 24: …s指針指向OBJC_CLASS_̲_Preson結構體地址。可以看出,cls指針指向的應該是分類的主類類對象的地址。分類源碼中將定義的對象方法、類方法、屬性等都存放到category_t結構體中。

_getObjc2CategoryList函數獲取到分類列表之後,進行遍歷,獲取其中的方法、協議、屬性等。最終都會調用remethododizeClass(cls)函數,該函數接受了類對象cls和分類數組cats, 而attachCategories(cls,cats,ture)函數會根據方法列表、屬性列表、協議列表、malloc分配內存,根據多少個分類以及每一塊方法需要多少內存來分配相應的內存地址。之後從分類數組裏面往三個數組裏面存放分類數組裏面存放的分類方法、屬性以及協議放入對應的mlist、proplists、protolosts數組中,這三個數組存放着所有分類的方法、屬性和協議。

然後通過class_rw_t中存放着類對象的方法、屬性和協議等數據,rw結構體通過類對象的data方法獲取,所以rw裏面存放這類對象裏面的數據。再通過rw調用方法列表、屬性列表、協議列表的attachList函數,將所有分類的方法、屬性、協議列表數組傳進去,attachList函數內部:
array()->lists:類對象原來的方法列表、屬性列表、協議列表。
addedLists:傳入所有分類的方法列表、屬性列表、協議列表。
attachLists函數中最重要的兩個方法:memmove內存移動和memcpy內存拷貝。
在這裏插入圖片描述

在這裏插入圖片描述

經過memmove和memcpy分類的方法、屬性、協議列表被放在了類對象中原本存儲的方法、屬性、協議列表前面。也就是說分類重寫本類的方法不會覆蓋本類的方法而是優先調用。
Category可以添加屬性,但並不會自動生成成員變量及set/get方法,分類在運行時纔會去加載,因此
分類中不可以添加成員變量


initialize是通過新消息發送機制調用的,消息發送機制通過isa指針找到對應的方法與實現,因此先找到分類方法中的實現,會優先調用分類方法中的實現;類在第一次接受到消息時,就會調用initialize,相當於第一次使用類的時候就會調用initialize方法。調用子類的initialize之前,會保證調用父類的initialize方法,如過之前已經調用過initialize,就不會調用initialize方法了。如果分類重寫initialize方法時,就會優先調用分類的方法。調用順序:
父類initialize方法 —若分類重寫initialize /或者/ 子類的initialize 均只會在第一次調用類的時候調用,不會再重複調用,
若一個類的分類實現了initialize,則不會再調用主類的initialize方法
若子類實現initialize方法,調用之前若父類沒有被調用過,會先去調用父類initialize ,若父類已調用過則不會調用父類的initialize
若子類沒有實現initialize方法,第一次調用子類就會調用其父類的initialize,所以父類的initialize可能會調用多次
若是有多個category實現了initialize,則根據build phases -> compile source 從上到下的編譯順序,最下方的category爲準,編譯是通過壓棧方式將category壓棧,後進先出,後編譯先調用

編譯時會根據編譯順序,先編譯的類就會優先調用load方法,在調用之前會優先調用父類的load,若分類中也有load會後於主類load方法調用,若多個category,多個category內部的load方法調用順序則是根據buildPhases->compile sources的順序,從上到下的編譯順序
Load則是直接拿load方法的內存地址直接調用,而不是通過消息發送機制調用。因此,分類中重寫load方法,只會先於主類方法調用,不會覆蓋,調用順序:
父類load —子類load —分類load

根據runtime的消息傳遞機制中的核心函數void objc_msgSend(id self,SEL cmd,…)來發送消息,先從當前類中查找調用的方法,若沒有找到則繼續從其父類中一層層往上找,那麼對於category重寫同一個方法,則在消息傳遞的過程中,會最先找到category中的方法並執行該方法。對於多個分類調用同一個方法,Xcode在運行時是根據buildPhases->Compile Sources裏面的從上至下順序編譯的,編譯時通過壓棧的方式將多個分類壓棧,根據後進先出的原則,後編譯的會被先調用,當objc_msgSend找到方法並調用之後,就不再繼續傳遞消息,所以形成所謂上的覆蓋。並不是後面創建的就一定被調用,得看創建之後其在buildPhases->Compile Sources裏面的位置


可參考 https://www.jianshu.com/p/fa66c8be42a2

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc Preson+Test.m

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章