程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> Objective-C Runtime機制詳解

Objective-C Runtime機制詳解

編輯:關於C語言

Objective-C Runtime機制詳解


Objective-C語言是一門動態語言,它將很多靜態語言在編譯和鏈接時做的事放到了運行時來處理。同時OC也是一門簡單的語言,很大一部分是C的內容,只是在語言層面上加了關鍵字和語法,真正讓OC強大的是它的運行時,它很小卻很強大,其中核心是消息分發。這種動態語言的優勢在於:我們寫代碼時更加靈活,如我們可以把消息轉發給我們想要的對象,或者隨意交換一個方法的實現。

這種特性意味著OC不僅需要一個編譯器,還需要一個運行時系統來執行編譯的代碼。對於OC來說,這個運行時系統就像一個操作系統一樣。這個運行時系統即Runtime,是用C和匯編寫的,這個庫使得C語言有了面向對象的能力。其中最主要的是消息機制。對於C語言,函數的調用在編譯的會決定調用哪個函數,編譯完成之後順序執行,無任何二義性。OC的函數調用稱為消息發送,屬於動態調用過程。在編譯的時候並不能決定真正的調用哪個函數(事實證明,在編譯階段,OC可以調用任何函數,即使這個函數並未實現,只要聲明過就不會報錯。而C語言在編譯階段就會報錯。)只有在真正運行的時候才會根據函數的名稱找到對應的函數來調用。

用一句話說:我們編寫的OC代碼,程序運行過程中,其實都是轉化為runtime的C語言代碼,runtime算是OC幕後工作者。runtime屬於OC的底層,可以進行非常底層的操作(用OC是無法實現的。)

Runtime庫主要做下面幾件事:

1.封裝:在這個庫中,對象可以用C語言中的結構體表示,而方法可以用C函數來實現,另外再加上一些額外的特性。這些結構體和函數被Runtime函數封裝後,我們就可以在程序運行時創建、檢查、修改類、對象和他們的方法了。

2.找出方法的最終執行代碼:當程序執行[object doSomething]時,會向消息接收者(object)發送一條消息(doSomething),Runtime會根據消息接收者是否能響應該消息而做出不同的反應。

 

OC的Runtime目前有兩個版本:Modern Runtime和Legacy runtime. Modern Runtime主要用於64位應用。Legacy runtime主要用於32位應用。目前一般都是64位了。

關於執行效率問題,“靜態語言執行效率要比動態語言高”應該是沒問題的。因為一部分的CPU計算損耗在Runtime中。

那OC是怎麼實現動態調用的呢?假如在OC中寫了如下代碼:

 

[obj makeText];

 

其中obj是一個對象,makeText是一個函數名稱。執行一個方法,有些語言,編譯器會執行一些額外的優化和錯誤檢查,因為調用關系很直接也很明顯。但是對於消息分發來說,就不那麼明顯了,在發消息之前不必知道某個對象是否能夠處理消息。你把消息發給他,他可能會處理,也可能轉給其他的Object來處理。一個消息不必對應一個方法,一個對象可能實現一個方法來處理多條消息。

在編譯時RunTime會將上述代碼轉化為:

 

objc_msgSend(obj,@selector(makeText));

 

所以其實

 

objc_msgSend(obj,@selector(makeText));
和
[obj makeText];
是等價的。


再比如:
[obj setTT:@111 isOK:YES];

objc_msgSend(obj,@selector(setTT:isOK:),@111,YES)
也是等價的。注意有參數的OC函數名的表達方式。

 



 

首先我們來看看obj這個對象,iOS中的obj都繼承與NSObject。

 

@interface NSObject{

 Class isa OBJC_ISA_AVAILABILITY

}

在NSObject中存在一個Class的isa指針。然後我們看看Class是什麼東西:這是在objc.h中定義的。

 

 

typedef struct objc_class *Class;
struct objc_class{

 Class isa; //指向metaclass


 Class super_class ;//指向父類

 const char *name;//類名

 long version;  //類的版本信息,初始化默認為0,可以通過runtime函數class_setVersion和class_getVersion進行修改讀取;

 long info;   //一些標識信息,如CLS_CLASS(0x1L)表示該類為普通的class,其中包含對象方法和成員變量;CLS_META(0x2L)表示該類為metaclass,其中包含類方法;

 long instance_size;  該類的實例變量大小(包括從父類繼承下來的實例變量);

 struct objc_ivar_list *ivars;  //用於存儲每個成員變量的地址;

 struct objc_method_list **methodLists;   //與info的一些標志位有關,如CLS_CLASS(0x1L),則存儲對象方法;

 struct objc_cache *cache;  //指向最近使用的方法的指針,用於提升效率;

 struct objc_protocol_list *protocols;  //存儲該類遵守的協議;
}

 

 

對於一個Class類中,存在很多東西,我們來解釋一下重要的東西:

Class isa :指向metaclass ,也就是靜態的Class。一般一個Obj對象中的isa會指向普通的Class,這個Class中存儲普通成員變量和對象方法(-開頭的方法),普通Class中的isa指針指向靜態Class,靜態Class中存儲static類型成員變量和類方法(+開頭的方法)。

 

Class super_class:指向父類,如果這個類是根類,則為NULL。

我們通過下面這個圖來了解類和對象的繼承關系:

\

 

注意:所有metaclass中isa指針都指向根metaclass(也就是Root Class Meta),而根metaclass則指向自身。Root metaclass是通過繼承Root Class產生的。與root class結構成員一致,也就是前面提到的結構。不同的是Root metaclass的isa指針指向自身。

 

然後再來看看方法:

@selector(makeText):這是一個SEL方法選擇器。SEL的主要作用就是通過方法名字(makeText)查找到對應方法的函數指針,然後調用其函數。SEL其本身是一個int類型的一個地址,地址中存放著方法的名字。對於一個類中,每一個方法對應著一個SEL。所以iOS類中不能存在2個名稱相同的方法,即使參數類型不同,因為SEL是根據方法名字生成的,相同的方法名稱只能對應一個SEL。同時,我們也應該知道,OC是沒有方法重載的。

 

我們再來看看具體的消息發送之後是怎麼樣來動態查找對應的方法的。

首先,編譯器將代碼[obj makeText];轉化為objc_msgSend(obj,@selector(makeText));在objc_msgSend函數中,首先通過obj的isa指針找到對應的class。在Class中先去cache中通過SEL查找對應函數method,若cache中未找到,再去methodList中查找,若methodList中未找到,則取superClass中查找。若能找到,則將method加入到cache中,以方便下次查找,並通過method中的函數指針跳轉到對應的函數中去執行。

class的方法列表其實是一個字典,key為selector,value為IMP函數指針。一個IMP是指向方法在內存中的實現。很重要的一點,selector和IMP之間的關系是在運行時才決定的,而不是編譯時。

 

對於面向對象而言,萬物皆對象,在OC中,類也是對象。The class is Object,也可以處理消息。所以你現在知道為什麼會有類方法和實例方法了。

 

Method Swizzling

我們上面講過,方法由兩個部分組成。selector相當於一個方法的id,IMP是方法的實現。這樣分開的一個遍歷就是selector和IMP之間的對應關系可以被改變。比如一個IMP可以有多個selectors指向它。

而Method Swizzling可以交換兩個方法的實現。在OC中,兩種擴展class的途徑。首先是subclass.你可以重寫某個方法,調用父類的實現,這也意味著你必須使用這個subclass的實例。但是我們如果使用Category分類,重寫某個方法之後,就不能再調用原來的方法了。

Method Swizzling可以搞定這個問題,你可以重寫某個方法而不用繼承,同時還可以調用原先的實現。通常的做法是在Category中添加一個方法,可以通過method_exchangeImplementations這個運行時方法來交換實現。

 

 

 

 

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved