1. 程式人生 > >ios學習路線—Objective-C(Runtime訊息機制)

ios學習路線—Objective-C(Runtime訊息機制)

RunTime簡稱執行時。就是系統在執行的時候的一些機制,其中最主要的是訊息機制。對於C語言,函式的呼叫在編譯的時候會決定呼叫哪個函式( C語言的函式呼叫請看這裡 )。編譯完成之後直接順序執行,無任何二義性。OC的函式呼叫成為訊息傳送。屬於動態呼叫過程。在編譯的時候並不能決定真正呼叫哪個函式(事實證明,在編 譯階段,OC可以呼叫任何函式,即使這個函式並未實現,只要申明過就不會報錯。而C語言在編譯階段就會報錯)。只有在真正執行的時候才會根據函式的名稱找到對應的函式來呼叫。

那OC是怎麼實現動態呼叫的呢?下面我們來看看OC通過傳送訊息來達到動態呼叫的祕密。假如在OC中寫了這樣的一個程式碼:

[obj makeText];

其中obj是一個物件,makeText是一個函式名稱。對於這樣一個簡單的呼叫。在編譯時RunTime會將上述程式碼轉化成

objc_msgSend(obj,@selector(makeText));

首先我們來看看obj這個物件,iOS中的obj都繼承於NSObject。

@interface NSObject <nsobject> {
    Class isa  OBJC_ISA_AVAILABILITY;
}

在NSObjcet中存在一個Class的isa指標。然後我們看看Class:

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類中,存在很多東西,下面我來一一解釋一下: 
Class isa:指向metaclass,也就是靜態的Class。一般一個Obj物件中的isa會指向普通的Class,這個Class中儲存普通成員變數和對 象方法(“-”開頭的方法),普通Class中的isa指標指向靜態Class,靜態Class中儲存static型別成員變數和類方法(“+”開頭的方 法)。

Class super_class:指向父類,如果這個類是根類,則為NULL。 
下面一張圖片很好的描述了類和物件的繼承關係: 
這裡寫圖片描述 
注意:所有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中的函式指標跳轉到對應的函式中去執行。