總結(jié):
通過源碼可以看到, 當(dāng)一個(gè)類在查找方法的時(shí)候, 會(huì)先判斷當(dāng)前類是否初始化, 如果沒有初始化就會(huì)去掉用initialize
方法
如果這個(gè)類的父類沒有初始化, 就會(huì)先調(diào)用父類的initialize
方法, 再調(diào)用自己的initialize
方法
類在調(diào)用initialize
時(shí), 使用的是objc_msgSend
消息機(jī)制調(diào)用
一、代碼準(zhǔn)備Demo地址
- 定義
Person
類, 繼承自NSObject
, 并實(shí)現(xiàn)+(void)initialize
方法
- 定義
Person
類的CategoryPerson+Test1
, 并實(shí)現(xiàn)+(void)initialize
方法
- 定義
Person
類的CategoryPerson+Test2
, 并實(shí)現(xiàn)+(void)initialize
方法
- 定義
Student
類, 繼承自Person
, 并實(shí)現(xiàn)+(void)initialize
方法
- 定義
Student
類的CategoryStudent+Test1
, 并實(shí)現(xiàn)+(void)initialize
方法
- 定義
Student
類的CategoryStudent+Test2
, 并實(shí)現(xiàn)+(void)initialize
方法
二竹祷、運(yùn)行程序
1彭沼、不主動(dòng)調(diào)用任何代碼, 運(yùn)行程序
-
main.m
中不添加任何代碼, 直接運(yùn)行程序
- 根據(jù)結(jié)果, 可以知道任何的
+(void)initialize
方法沒有被調(diào)用
2恋沃、Person
類調(diào)用alloc
方法
-
main.m
中調(diào)用[Person alloc]
, 底層相當(dāng)于
objc_msgSend([Person class], @selector(alloc));
復(fù)制代碼
`
- 可以發(fā)現(xiàn),
Person+Test1
的代碼被調(diào)用了 - 查看一下文件的編譯順序, 可以發(fā)現(xiàn)
Person+Test1
比Person+Test2
編譯的晚
- 這說明在
Person
類通過消息機(jī)制調(diào)用方法時(shí), 會(huì)通過消息機(jī)制調(diào)用+(void)initialize
方法
objc_msgSend([Person class], @selector(initialize))
復(fù)制代碼
3掠兄、Student
類調(diào)用alloc
方法
-
main.m
中調(diào)用[Student alloc]
, 底層相當(dāng)于
objc_msgSend([Student class], @selector(alloc));
復(fù)制代碼
- 可以看到
[Student alloc]
調(diào)用時(shí), 會(huì)先調(diào)用Person
類的+(void)initialize
方法, 在調(diào)用Student
類的+(void)initialize
方法
推論: 當(dāng)一個(gè)類在第一次接受消息時(shí), 會(huì)調(diào)用他自己的
+(void)initialize
方法, 如果他有父類, 那么就會(huì)優(yōu)先調(diào)用父類的+(void)initialize
方法
三毙驯、查看關(guān)于類調(diào)用+(void)initialize
方法的源碼
- 在源碼里搜索
Method class_getInstanceMethod(Class cls, SEL sel)
函數(shù)
- 找到代碼
lookUpImpOrNil(cls, sel, nil, NO, NO, YES);
- 進(jìn)入
IMP lookUpImpOrNil(Class cls, SEL sel, id inst, bool initialize, bool cache, bool resolver)
函數(shù)后, 找到IMP imp = lookUpImpOrForward(cls, sel, inst, initialize, cache, resolver);
- 進(jìn)入
IMP lookUpImpOrForward(Class cls, SEL sel, id inst, bool initialize, bool cache, bool resolver)
函數(shù)中
- 可以看到有判斷, 如果需要初始化, 并且類沒有初始化, 那么調(diào)用
_class_initialize (_class_getNonMetaClass(cls, inst));
- 進(jìn)入
void _class_initialize(Class cls)
函數(shù)中, 我們可以看到, 如果被傳入的類有父類, 并且父類沒有初始化, 會(huì)通過遞歸將父類傳入
- 向下可以找到代碼
callInitialize(cls);
, 即: 調(diào)用初始化方法, 并傳入類對(duì)象(如果有父類, 父類沒有初始化的情況下, 會(huì)先初始化父類)
- 進(jìn)入
void callInitialize(Class cls)
函數(shù), 可以看到, 底層是通過消息機(jī)制, 調(diào)用了類對(duì)象的initialize
方法
總結(jié):
通過源碼可以看到, 當(dāng)一個(gè)類在查找方法的時(shí)候, 會(huì)先判斷當(dāng)前類是否初始化, 如果沒有初始化就會(huì)去掉用initialize
方法
如果這個(gè)類的父類沒有初始化, 就會(huì)先調(diào)用父類的initialize
方法, 再調(diào)用自己的initialize
方法
類在調(diào)用initialize
時(shí), 使用的是objc_msgSend
消息機(jī)制調(diào)用
- 所以, 在調(diào)用
[Person alloc]
方法時(shí), 會(huì)使用消息機(jī)制
調(diào)用Person
類的initialize
方法, 又因?yàn)?code>Person存在兩個(gè)CategoryPerson+Test1
和Person+Test2
, 此時(shí)就看哪一個(gè)Category
最后一個(gè)編譯, 就會(huì)調(diào)用里面的initialize
方法 - 在調(diào)用
[Student alloc]
時(shí), 會(huì)調(diào)用Student
類的initialize
方法, 但是因?yàn)榇藭r(shí)父類Person
還沒有初始化, 所以會(huì)先調(diào)用Person
的initialize
方法
四闻伶、移除子類的+(void)initialize
方法, 再次給子類發(fā)送消息
- 刪除
Student
丈甸、Student+Test1
和Student+Test2
中的initialize
方法
- 可以看到,
Person (Test1) - initialize
打印了兩次, 說明Person
的initialize
方法被調(diào)用了兩次 - 前面已經(jīng)說過, 當(dāng)
Student
類在第一次接收消息時(shí), 會(huì)進(jìn)行初始化, 如果父類沒有初始化, 會(huì)先給父類初始化 - 所以, 第一次的
Person (Test1) - initialize
打印, 實(shí)際就是Person
類初始化時(shí)調(diào)用的 - 而一個(gè)類只能初始化一次, 所以第二次的打印, 實(shí)際是
Student
類初始化時(shí)調(diào)用的 -
Student
初始化調(diào)用initialize
方法時(shí), 用的下面方式
objc_msgSend([Student class], @selector(initialize))
- 在OC中, 使用消息機(jī)制調(diào)用類方法時(shí), 調(diào)用順序如下:
-
類對(duì)象
通過isa
找到元類對(duì)象
, 在元類對(duì)象
的方法列表中查找方法, 如果有就會(huì)調(diào)用 - 如果
元類對(duì)象
中沒有調(diào)用的方法, 就會(huì)通過元類對(duì)象
的superclass
找到父類
的元類對(duì)象
, 接著父類
的元類對(duì)象
的方法列表中查找方法, 如果有就會(huì)調(diào)用
-
- 所以,
Student
實(shí)際上在初始化時(shí), 調(diào)用的是objc_msgSend([Student class], @selector(initialize))
, 但是因?yàn)?code>Student并沒有實(shí)現(xiàn)initialize
方法, 所以Student
調(diào)用了父類Person
的initialize
方法
轉(zhuǎn)載文章:(防止資源地址流失,記錄文章)
# 小碼哥iOS學(xué)習(xí)筆記第六天: initialize方法