標籤:style blog http io color os ar 使用 java
在入門層級的ObjC 教程中,我們常對從C++或Java 或其他物件導向語言轉過來的程式員說,ObjC 中的方法調用(ObjC中的術語為訊息)跟其他語言中的方法調用差不多,只是形式有些不同而已。
譬如C++ 中的:
Bird * aBird = new Bird();
aBird->fly();
在ObjC 中則如下:
Bird * aBird = [[Bird alloc] init];
[aBird fly];
初看起來,好像只是書寫形式不同而已,實則差異大矣。C++中的方法調用可能是動態,也可能是靜態;而ObjC中的訊息都為動態。下文將詳細介紹為什麼是動態,以及編譯器在這背後做了些什麼事情。
class
1 struct objc_class { 2 Class isa OBJC_ISA_AVAILABILITY; 3 4 #if !__OBJC2__ 5 Class super_class OBJC2_UNAVAILABLE; 6 const char *name OBJC2_UNAVAILABLE; 7 long version OBJC2_UNAVAILABLE; 8 long info OBJC2_UNAVAILABLE; 9 long instance_size OBJC2_UNAVAILABLE;10 struct objc_ivar_list *ivars OBJC2_UNAVAILABLE;11 struct objc_method_list **methodLists OBJC2_UNAVAILABLE;12 struct objc_cache *cache OBJC2_UNAVAILABLE;13 struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;14 #endif15 16 } OBJC2_UNAVAILABLE;17 /* Use `Class` instead of `struct objc_class *` */
由此可見,Class 是指向類結構體的指標,該類結構體含有一個指向其父類類結構的指標,該類方法的鏈表,該類方法的緩衝以及其他必要資訊。
NSObject 的class 方法就返回這樣一個指向其類結構的指標。每一個類執行個體對象的第一個執行個體變數是一個指向該對象的類結構的指標,叫做isa。通過該指標,對象可以訪問它對應的類以及相應的父類。一所示:
一所示,圓形所代表的執行個體對象的第一個執行個體變數為 isa,它指向該類的類結構 The object’s class。而該類結構有一個指向其父類類結構的指標superclass, 以及自身訊息名稱(selector)/實現地址(address)的方法鏈表。
方法的含義:
注意這裡所說的方法鏈表裡面儲存的是Method 類型的。圖一中selector 就是指 Method的 SEL, address就是指Method的 IMP。 Method 在標頭檔 objc_class.h中定義如下:
typedef struct objc_method *Method;
typedef struct objc_ method {
SEL method_name;
char *method_types;
IMP method_imp;
};
一個方法 Method,其包含一個方法選標 SEL – 表示該方法的名稱,一個types – 表示該方法參數的類型,一個 IMP - 指向該方法的具體實現的函數指標。
SEL 的含義:
在前面我們看到方法選標 SEL 的定義為:
typedef struct objc_selector *SEL;
它是一個指向 objc_selector 指標,表示方法的名字/簽名。如下所示,列印出 selector。
-(NSInteger)maxIn:(NSInteger)a theOther:(NSInteger)b
{
return (a > b) ? a : b;
}
NSLog(@"SEL=%s", @selector(maxIn:theOther:));
輸出:SEL=maxIn:theOther:
不同的類可以擁有相同的 selector,這個沒有問題,因為不同類的執行個體對象performSelector相同的 selector 時,會在各自的訊息選標(selector)/實現地址(address) 方法鏈表中根據 selector 去尋找具體的方法實現IMP, 然後用這個方法實現去執行具體的實現代碼。這是一個動態綁定的過程,在編譯的時候,我們不知道最終會執行哪一些代碼,只有在執行的時候,通過selector去查詢,我們才能確定具體的執行代碼。
IMP 的含義:
在前面我們也看到 IMP 的定義為:
typedef id (*IMP)(id, SEL, ...);
根據前面id 的定義,我們知道 id是一個指向 objc_object 結構體的指標,該結構體只有一個成員isa,所以任何繼承自 NSObject 的類對象都可以用id 來指代,因為 NSObject 的第一個成員執行個體就是isa。
至此,我們就很清楚地知道 IMP 的含義:IMP 是一個函數指標,這個被指向的函數包含一個接收訊息的對象id(self 指標), 調用方法的選標 SEL (方法名),以及不定個數的方法參數,並返回一個id。也就是說 IMP 是訊息最終調用的執行代碼,是方法真正的實現代碼 。我們可以像在C語言裡面一樣使用這個函數指標。
NSObject 類中的methodForSelector:方法就是這樣一個擷取指向方法實現IMP 的指標,methodForSelector:返回的指標和賦值的變數類型必須完全一致,包括方法的參數類型和傳回值類型。
下面的例子展示了怎麼使用指標來調用setFilled:的方法實現:
void (*setter)(id, SEL, BOOL);
int i;
setter = (void(*)(id, SEL, BOOL))[target methodForSelector:@selector(setFilled:)];
for (i = 0; i < 1000; i++)
setter(targetList[i], @selector(setFilled:), YES);
使用methodForSelector:來避免動態綁定將減少大部分訊息的開銷,但是這隻有在指定的訊息被重複發送很多次時才有意義,例如上面的for迴圈。
注意,methodForSelector:是Cocoa運行時系統的提供的功能,而不是Objective-C語言本身的功能。
ios底層開發訊息機制(一)基本概念