Objective-C Runtime

最近在學Objective-C的多線程,但是網上別人說先花點時間研究Objective-C的runtime機制會更利於理解多線程裏面的很多東西。

簡介

Runtime(運行時),其實是一個很單純的一個概念。根據蘋果官方文檔,Objective-C儘可能地將以前在編譯和鏈接階段做出地決定推遲到程序運行時(runtime)做出決定。runtime只是指代碼執行的這個階段,就像編譯階段和鏈接階段一樣。這樣是爲了動態(Dynamically)的解決事務。我們先思考,何爲Dynamic。Objective-C只是具備動態特性,而並不是動態語言或者動態類型語言。下面舉例一段代碼:
    id obj = self;
    if ([obj respondsToSelector:@selector(doSomething)]) {
        [obj performSelector:@selector(doSomething) withObject:Nil afterDelay:0];
    }
    
在編譯階段,對於編譯器來說,它只知道obj是一個id指針,並不知道obj能夠具體幹哪些活。假如我們直接[obj doSomething]; 很明顯,編譯器認爲obj並沒有doSomething這個方法,會直接報錯。而有runtime system之後,如上地代碼就很好地解決了這個問題。runtime system會檢查obj能不能接收doSomething這個消息,然後再確定是否進行函數地調用。
那麼爲什麼要將obj給聲明爲id呢?直接給它確切地某一個類型不是更好?顯然,這樣有一個好處就是,多態。obj可以是很多不同地類地對象,而我們也不用詳細了obj是具體的哪個類,這個類的結構。這樣做既降低了耦合又增強了代碼的處理能力。

所以,Objective-C不僅有一個編譯器,還有一個運行時系統(runtime system)來執行編譯後的代碼。

runtime的應用

Objective-C在3個層面上與runtime system進行交互:
1. 源代碼
2. 在NSObject中定義的方法
3. 直接去調用runtime functions

源代碼:

在大多數情況下,runtime system的工作的是自動且不可見的。我們僅在編寫代碼和編譯代碼時用到它。當在編譯代碼時,編譯器會生成一些數據結構和函數來實現動態的特性,而這對程序員是透明的。

NSObject 的方法

在我們應用中,大多數的Cocoa的類都繼承自NSObject。而一些NSObject的方法可以很簡單向runtime system查詢一些我們需要的信息。
1. respondsToSelector:
2. isKindOfClass:
3. isMemberOfClass:
4. conformsToProtocol:

Runtime Functions

runtime functions 可以使我們用C語言做到很多編譯器在我們編寫Objective-C程序時做的事,但是我們平時在寫Objective-C代碼時,一般都不會用到runtime functions

消息

在Objective-C中,消息(message)和具體的方法的實現是直到運行時才綁定到一起的。編譯器對消息做如下處理
1. [receiver message];
2. objc_msgSend(receiver, selector);
3. objc_msgSend(receiver, selector, arg1,arg2...);

編譯器將1.裏面的消息轉化爲2.裏面的消息函數的調用,當有參數時,則轉化爲形如3.的格式。
消息函數(objc_msgSend)來實現動態的綁定
1. 因爲相同的方法可以被不同的類實現,所以具體的實現過程是由objc_msgSend(receiver, selector)裏面的receiver決定的
2. 然後消息函數調用確定了的具體實現過程,並將參數一併傳入
3. 消息函數將具體實現過程的返回值作爲自己的返回值返回。
我們確定消息函數的具體實現則依賴於每個類和對象的結構,每個類的結構都包含以下兩個部分:
1. 一個指向父類的指針
2. 一個類調度表(dispatch table), 調度表將方法的標識和對應的方法地址給聯繫起來。
當一個對象創建之後,系統爲其分配了內存,初始化了變量。第一個初始化的是一個指向這個對象的類結構的一個指針,我們稱爲isa指針。isa指針可以使我們可以通過對象找到其對應的類,並通過這個類,訪問到所有其繼承的類。

當一條消息被髮送給一個對象,消息函數(msgSend)也跟着此對象的isa指針訪問到了這個對象的類。消息函數遍歷這個類的dispatch table。如果沒有找到需要的selelctor,那麼消息函數會跟着這個類指向父類的指針,進入父類進行同樣的工作,直到找到繼承樹的最頂端。如果一旦消息函數找到了需要的selector。那麼這個函數就直接通過dispatch table進行方法的具體實現。
這就是在運行時確定方法的具體實現。

目前我知道的也就這麼多了,如果有不正確的地方希望能夠指出,才學了半年,也希望多多交流

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