標籤:io os ar 使用 for sp strong on 代碼
上一章節對基礎概念有了些瞭解,我們對ObjC 中的訊息應該有個大致思路了:樣本
Bird * aBird = [[Bird alloc] init];
[aBird fly];
中對 fly 的調用,編譯器通過插入一些代碼,將之轉換為對方法具體實現IMP的調用,這個 IMP是通過在 Bird 的類結構中的方法鏈表中尋找名稱為fly 的 選標SEL 對應的具體方法實現找到的。
上面的思路還有一些沒有提及的話題,比如說編譯器插入了什麼代碼,如果在方法鏈表中沒有找到對應的 IMP又會如何,這些話題在下面展開。
訊息函數 obj_msgSend:
編譯器會將訊息轉換為對訊息函數 objc_msgSend的調用,該函數有兩個主要的參數:訊息接收者id 和訊息對應的方法選標 SEL, 同時接收訊息中的任意參數:
id objc_msgSend(id theReceiver, SELtheSelector, ...)
如上面的訊息 [aBird fly]會被轉換為如下形式的函數調用:
objc_msgSend(aBird, @selector(fly));
該訊息函數做了動態綁定所需要的一切工作:
1,它首先找到 SEL 對應的方法實現 IMP。因為不同的類對同一方法可能會有不同的實現,所以找到的方法實現依賴於訊息接收者的類型。
2, 然後將訊息接收者對象(指向訊息接收者對象的指標)以及方法中指定的參數傳遞給方法實現 IMP。
3, 最後,將方法實現的傳回值作為該函數的傳回值返回。
編譯器會自動插入調用該訊息函數objc_msgSend的代碼,我們無須在代碼中顯示調用該訊息函數。當objc_msgSend找到方法對應的實現時,它將直接調用該方法實現,並將訊息中所有的參數都傳遞給方法實現,同時,它還將傳遞兩個隱藏的參數:訊息的接收者以及方法名稱 SEL。這些參數協助方法實現獲得了訊息運算式的資訊。它們被認為是”隱藏“的是因為它們並沒有在定義方法的原始碼中聲明,而是在代碼編譯時間是插入方法的實現中的。
儘管這些參數沒有被顯示聲明,但在原始碼中仍然可以引用它們(就象可以引用訊息接收者對象的執行個體變數一樣)。在方法中可以通過self來引用訊息接收者對象,通過選標_cmd來引用方法本身。在下面的例子中,_cmd 指的是strange方法,self指的收到strange訊息的對象。
- strange
{
id target = getTheReceiver();
SEL method = getTheMethod();
if (target == self || mothod == _cmd)
return nil;
return [target performSelector:method];
}
在這兩個參數中,self更有用一些。實際上,它是在方法實現中訪問訊息接收者對象的執行個體變數的途徑。
尋找 IMP 的過程:
前面說了,objc_msgSend 會根據方法選標 SEL 在類結構的方法列表中尋找方法實現IMP。這裡頭有一些文章,我們在前面的類結構中也看到有一個叫objc_cache *cache 的成員,這個緩衝為提高效率而存在的。每個類都有一個獨立的緩衝,同時包括繼承的方法和在該類中定義的方法。。
尋找IMP 時:
1,首先去該類的方法 cache 中尋找,如果找到了就返回它;
2,如果沒有找到,就去該類的方法列表中尋找。如果在該類的方法列表中找到了,則將 IMP 返回,並將它加入cache中緩衝起來。根據最近使用原則,這個方法再次調用的可能性很大,緩衝起來可以節省下次調用再次尋找的開銷。3,3,如果在該類的方法列表中沒找到對應的 IMP,在通過該類結構中的 super_class指標在其父類結構的方法列表中去尋找,直到在某個父類的方法列表中找到對應的IMP,返回它,並加入cache中。
4,如果在自身以及所有父類的方法列表中都沒有找到對應的 IMP,則進入下文中要講的訊息轉寄流程。
便利函數:
我們可以通過NSObject的一些方法擷取運行時資訊或動態執行一些訊息:
class 返回對象的類;
isKindOfClass 和 isMemberOfClass檢查對象是否在指定的類繼承體系中;
respondsToSelector 檢查對象能否相應指定的訊息;
conformsToProtocol 檢查對象是否實現了指定協議類的方法;
methodForSelector 返回指定方法實現的地址。
performSelector:withObject 執行SEL 所指代的方法。
ios底層開發訊息機制(二)訊息調用過程