消息
- 體會蘋果官方文檔中的 messages aren’t bound to method implementations until Runtime世蔗。消息直到運行時才會與方法實現(xiàn)進行綁定宁否。
- 我理解的是:源碼通過預(yù)處理 ->編譯->鏈接->匯編->生成可執(zhí)行文件,在由shell(暫且理解為shell)解釋執(zhí)行的時候才與方法的實現(xiàn)進行綁定,
- 如何綁定:如果是實例方法每個實例的isa指針指向該實例對應(yīng)的類,在這個類中有一個分發(fā)表,在這個表中存儲著一個SEL(理解為方法編號)對應(yīng)一個IMP(函數(shù)指針),通過SEL找到對應(yīng)的IMP,再將IP指針指向該函數(shù)地址執(zhí)行
1榆苞、objc_msgSend
- 這里要清楚一點格郁,objc_msgSend 方法看起來好像返回了數(shù)據(jù),其實objc_msgSend 從不返回數(shù)據(jù),而是你的方法在運行時** 實現(xiàn) ** ** 被調(diào)用后 **才會返回數(shù)據(jù)。下面詳細敘述消息發(fā)送的步驟(如下圖):
消息轉(zhuǎn)發(fā)順序
- 首先檢測這個* selector *是不是要忽略,比如 Mac OS X 開發(fā)腾降,有了垃圾回收就不理會 retain,release 這些函數(shù)碎绎。
檢測這個 selector 的 target 是不是 nil蜂莉,Objc 允許我們對一個 nil 對象執(zhí)行任何方法不會 Crash,因為運行時會被忽略掉
如果上面兩步都通過了混卵,那么就開始查找這個類的實現(xiàn) IMP,先從 cache 里查找窖张,如果找到了就運行對應(yīng)的函數(shù)去執(zhí)行相應(yīng)的代碼幕随。
如果 cache 找不到就找類的方法列表中是否有對應(yīng)的方法。
如果類的方法列表中找不到就到父類的方法列表中查找宿接,一直找到 NSObject 類為止赘淮。
如果還找不到辕录,就要開始進入動態(tài)方法解析了
消息轉(zhuǎn)發(fā)函數(shù),編譯器會根據(jù)情況在以下四個方法中選擇一個調(diào)用
/** 如果消息是傳遞給父類,那么會調(diào)用名字帶有 Super 的函數(shù)梢卸,如果消息返回值是數(shù)據(jù)結(jié)構(gòu)而不是簡單值時走诞,會調(diào)用名字帶有 stret 的函數(shù)。 */
objc_msgSend
objc_msgSend_stret
objc_msgSendSuper
objc_msgSendSuper_stret
2蛤高、方法中的隱藏參數(shù)
- ** 我們經(jīng)常用到關(guān)鍵字 self 蚣旱,但是 self 是如何獲取當前方法的對象呢? **
其實戴陡,這也是 Runtime 系統(tǒng)的作用塞绿,self 實在方法運行時被動態(tài)傳入的。當 objc_msgSend 找到方法對應(yīng)實現(xiàn)時恤批,它將直接調(diào)用該方法實現(xiàn)异吻,并將消息中所有參數(shù)都傳遞給方法實現(xiàn),同時喜庞,它還將傳遞兩個隱藏參數(shù): - 接受消息的對象(self 所指向的內(nèi)容诀浪,當前方法的對象指針)
- 方法選擇器(_cmd 指向的內(nèi)容,當前方法的 SEL 指針)
這兩個參數(shù)中延都, self更實用雷猪。它是在** 方法實現(xiàn)中 訪問 消息接收者對象 的實例變量的 途徑 **(不是很理解)
3、獲取方法的地址
- NSObject 類中有一個實例方法:methodForSelector窄潭,你可以用它來獲取某個方法選擇器對應(yīng)的 IMP 春宣,舉個例子:
/* 定義一個函數(shù)指針 */
void (*setter)(id, SEL, BOOL);
int i;
/* 給函數(shù)指針賦值 */
setter = (void (*)(id, SEL, BOOL))[target methodForSelector:@selector(setFilled:)];
/* 通過函數(shù)指針調(diào)用1000次這個方法 */
for ( i = 0 ; i < 1000 ; i++ ){
setter(targetList[i], @selector(setFilled:), YES);
}
** 注意:上面用到的methodForSelector:方法是由 Runtime 系統(tǒng)提供的,而不是 Objc 自身的特性 **