分類Category
一、 Category的底層結構
- 定義在objc-runtime-new.h中
二、 Category的加載處理過程
2.1 加載順序
- 通過Runtime加載某個類的所有Category數據
- 把所有Category的方法、屬性、協議數據,合併到一個大數組中
後面參與編譯的Category數據,會在數組的前面 - 將合併後的分類數據(方法、屬性、協議),插入到類原來數據的前面
2.2 源碼解讀順序
-
objc-os.mm
- _objc_init
- map_images
- map_images_nolock
-
objc-runtime-new.mm
- _read_images
- remethodizeClass
- attachCategories
- attachLists
- realloc、memmove、 memcpy
三、+ load方法
-
+load
方法會在runtime加載類、分類
時調用 -
每個
類、分類
的+load,在程序運行過程中只調用一次 -
調用順序
- 先調用類的+load
- 按照編譯先後順序調用(先編譯,先調用)
- 調用子類的+load之前會先調用父類的+load
- 再調用分類的+load
- 按照編譯先後順序調用(先編譯,先調用)
-
objc4源碼解讀過程:objc-os.mm
-
_objc_init
-
load_images
-
prepare_load_methods
- schedule_class_load
- add_class_to_loadable_list
- add_category_to_loadable_list
-
call_load_methods
- call_class_loads
- call_category_loads
- (*load_method)(cls, SEL_load)
-
-
+load方法是根據方法地址直接調用,並不是經過 objc_msgSend函數調用
四、+initialize方法
-
+initialize方法會在類第一次接收到消息時調用
-
調用順序
- 先調用父類的+initialize, 再調用子類的+initialize
- (先初始化父類, 再初始化子類, 每個類只會初始化1次)
-
objc4源碼解讀過程
- objc-msg-arm64.s
- objc_msgSend
- objc-runtime-new.mm
- class_getInstanceMethod
- lookUpImpOrNil
- lookUpImpOrForward
- _class_initialize
- callInitialize
- objc_msgSend(cls, SEL_initialize)
- objc-msg-arm64.s
-
initialize內部實現邏輯
// 在子類中, 會判斷父類是否已經初始化過, 沒有會調用一次父類的初始化
objc_msgSend([MJPerson class], @selector(initialize));
objc_msgSend([MJStudent class], @selector(initialize));
// MJPerson (Test2) +initialize
objc_msgSend([MJPerson class], @selector(initialize));
// MJPerson (Test2) +initialize
objc_msgSend([MJStudent class], @selector(initialize));
// MJPerson (Test2) +initialize
objc_msgSend([MJTeacher class], @selector(initialize));
- +initialize和+load的很大區別是, +initialize是通過objc_msgSend進行調用的, 所以有以下特點
- 如果子類沒有實現+initialize, 會調用父類的+initialize(所以父類的+initialize可能會被調用多次)
- 如果分類實現了+initialize, 就覆蓋類本身的+initialize調用
五、總結
load、initialize方法的區別什麼?
-
調用方式
1> load是根據函數地址直接調用
2> initialize是通過objc_msgSend調用 -
調用時刻
1> load是runtime加載類、分類的時候調用(只會調用1次)
2> initialize是類第一次接收到消息的時候調用,每一個類只會initialize一次(父類的initialize方法可能會被調用多次)
load、initialize的調用順序?
-
load
1> 先調用類的load
a) 先編譯的類,優先調用load
b) 調用子類的load之前,會先調用父類的load2> 再調用分類的load
a) 先編譯的分類,優先調用load -
initialize
1> 先初始化父類
2> 再初始化子類(可能最終調用的是父類的initialize方法)