runtime

本文轉自:http://www.cocoachina.com/ios/20141018/9960.htmlhtml

 

最近在找工做,Objective-C中的Runtime是常常被問到的一個問題,幾乎是面試大公司必問的一個問題。固然還有一些其餘問題也幾乎必問,例 如:RunLoop,Block,內存管理等。其餘的問題若是有機會我會在其餘文章中介紹。本篇文章主要介紹RunTime。ios

RunTime簡稱運行時。就是系統在運行的時候的一些機制,其中最主要的是消息機制。對於C語言,函數的調用在編譯的時候會決定調用哪一個函數( C語言的函數調用請看這裏 )。編譯完成以後直接順序執行,無任何二義性。OC的函數調用成爲消息發送。屬於動態調用過程。在編譯的時候並不能決定真正調用哪一個函數(事實證實,在編 譯階段,OC能夠調用任何函數,即便這個函數並未實現,只要申明過就不會報錯。而C語言在編譯階段就會報錯)。只有在真正運行的時候纔會根據函數的名稱找 到對應的函數來調用。面試

那OC是怎麼實現動態調用的呢?下面咱們來看看OC經過發送消息來達到動態調用的祕密。假如在OC中寫了這樣的一個代碼:函數

1
[obj makeText];

其中obj是一個對象,makeText是一個函數名稱。對於這樣一個簡單的調用。在編譯時RunTime會將上述代碼轉化成oop

1
objc_msgSend(obj,@selector(makeText));

首先咱們來看看obj這個對象,iOS中的obj都繼承於NSObject。spa

1
2
3
@interface NSObject <nsobject> {
     Class isa  OBJC_ISA_AVAILABILITY;
}</nsobject>

在NSObjcet中存在一個Class的isa指針。而後咱們看看Class:指針

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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),則存儲對象方法,如CLS_META (0x2L),則存儲類方法;
   struct objc_cache *cache;  // 指向最近使用的方法的指針,用於提高效率;
   struct objc_protocol_list *protocols;  // 存儲該類遵照的協議
     }

 

咱們能夠看到,對於一個Class類中,存在不少東西,下面我來一一解釋一下:code

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

Class super_class:指向父類,若是這個類是根類,則爲NULL。對象

下面一張圖片很好的描述了類和對象的繼承關係:

iuqQFnm.png

注意:全部metaclass中isa指針都指向跟metaclass。而跟metaclass則指向自身。Root metaclass是經過繼承Root class產生的。與root class結構體成員一致,也就是前面提到的結構。不一樣的是Root metaclass的isa指針指向自身。

Class類中其餘的成員這裏就先不作過多解釋了,下面咱們來看看:

@selector (makeText):這是一個SEL方法選擇器。SEL其主要做用是快速的經過方法名字(makeText)查找到對應方法的函數指針,而後調用其函 數。SEL其自己是一個Int類型的一個地址,地址中存放着方法的名字。對於一個類中。每個方法對應着一個SEL。因此iOS類中不能存在2個名稱相同 的方法,即便參數類型不一樣,由於SEL是根據方法名字生成的,相同的方法名稱只能對應一個SEL。

下面咱們就來看看具體消息發送以後是怎麼來動態查找對應的方法的。

首先,編譯器將代碼[obj makeText];轉化爲objc_msgSend(obj, @selector (makeText));,在objc_msgSend函數中。首先經過obj的isa指針找到obj對應的class。在Class中先去cache中 經過SEL查找對應函數method(猜想cache中method列表是以SEL爲key經過hash表來存儲的,這樣能提升函數查找速度),若 cache中未找到。再去methodList中查找,若methodlist中未找到,則取superClass中查找。若能找到,則將method加 入到cache中,以方便下次查找,並經過method中的函數指針跳轉到對應的函數中去執行。

相關文章
相關標籤/搜索