刨根問底Objective-C Runtime(2)- Object & Class & Meta Class
上一篇筆記講述了objc runtime中Self 和 Super的細節,本篇筆記主要是講述objc runtime中關於Object & Class & Meta Class的細節。
習題內容
下面程式碼的執行結果是?
@interface Sark : NSObject @end @implementation Sark @end int main(int argc, const char * argv[]) { @autoreleasepool { BOOL res1 = [(id)[NSObject class] isKindOfClass:[NSObject class]]; BOOL res2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]]; BOOL res3 = [(id)[Sark class] isKindOfClass:[Sark class]]; BOOL res4 = [(id)[Sark class] isMemberOfClass:[Sark class]]; NSLog(@"%d %d %d %d", res1, res2, res3, res4); } return 0; }
執行結果為:
2014-11-05 14:45:08.474 Test[9412:721945] 1 0 0 0
這裡先看幾個概念
什麼是 id
id 在 objc.h 中定義如下:
/// A pointer to an instance of a class.
typedef struct objc_object *id;
就像註釋中所說的這樣 id 是指向一個 objc_object 結構體的指標。
id
這個struct的定義本身就帶了一個 *, 所以我們在使用其他NSObject型別的例項時需要在前面加上 *, 而使用 id 時卻不用。
那麼objc_object又是什麼呢
objc_object
/// Represents an instance of a class.
struct objc_object {
Class isa;
};
這個時候我們知道Objective-C中的object在最後會被轉換成C的結構體,而在這個struct中有一個 isa 指標,指向它的類別 Class。
那麼什麼是Class呢
在 objc.h 中定義如下:
/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;
我們可以看到 Class本身指向的也是一個C的struct objc_class
繼續看在runtime.h中objc_class
定義如下:
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE;
const char *name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE;
struct objc_method_list **methodLists OBJC2_UNAVAILABLE;
struct objc_cache *cache OBJC2_UNAVAILABLE;
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
該結構體中,isa 指向所屬Class, super_class指向父類別。
繼續看
下載objc原始碼,在 objc-runtime-new.h 中,我們發現 objc_class
有如下定義:
struct objc_class : objc_object {
// Class ISA;
Class superclass;
...
...
}
豁然開朗,我們看到在Objective-C的設計哲學中,一切都是物件。Class在設計中本身也是一個物件。而這個Class物件的對應的類,我們叫它 Meta
Class
。即Class結構體中的 isa 指向的就是它的 Meta
Class
。
Meta Class
根據上面的描述,我們可以把Meta
Class
理解為 一個Class物件的Class
。簡單的說:
- 當我們傳送一個訊息給一個NSObject物件時,這條訊息會在物件的類的方法列表裡查詢
- 當我們傳送一個訊息給一個類時,這條訊息會在類的Meta Class的方法列表裡查詢
而 Meta Class本身也是一個Class,它跟其他Class一樣也有自己的 isa 和 super_class 指標。看下圖:
- 每個Class都有一個isa指標指向一個唯一的Meta Class
- 每一個Meta Class的isa指標都指向最上層的Meta Class(圖中的NSObject的Meta Class)
最上層的Meta Class的isa指標指向自己,形成一個迴路
- 每一個Meta Class的super class指標指向它原本Class的 Super Class的Meta Class。
但是最上層的Meta Class的 Super Class指向NSObject Class本身
- 最上層的NSObject Class的super class指向 nil
解惑
為了更加清楚的知道整個函式呼叫過程,我們使用clang
-rewrite-objc main.m
重寫,可獲得如下程式碼:
BOOL res1 = ((BOOL (*)(id, SEL, Class))(void *)objc_msgSend)((id)((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("class")), sel_registerName("isKindOfClass:"), ((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("class")));
BOOL res2 = ((BOOL (*)(id, SEL, Class))(void *)objc_msgSend)((id)((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("class")), sel_registerName("isMemberOfClass:"), ((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("class")));
BOOL res3 = ((BOOL (*)(id, SEL, Class))(void *)objc_msgSend)((id)((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Sark"), sel_registerName("class")), sel_registerName("isMemberOfClass:"), ((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("class")));
BOOL res4 = ((BOOL (*)(id, SEL, Class))(void *)objc_msgSend)((id)((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Sark"), sel_registerName("class")), sel_registerName("isMemberOfClass:"), ((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("class")));
先看前兩個呼叫:
- 最外層是
objc_msgSend
函式,轉發訊息。 - 函式第一個引數是
(id)((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("class"))
- 函式第二個引數是轉發的selector
- 函式第三個引數是
((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("class"))
我們注意到第一個引數和第三個引數對應重寫的是[NSObject
class]
,即使用objc_msgSend
向
NSObject Class 傳送 @selector(class) 這個訊息
開啟objc原始碼,在 Object.mm 中發現+
(Class)class
實現如下:
+ (Class)class {
return self;
}
所以即返回Class類的物件本身。看如下輸出:
NSLog(@"%p", [NSObject class]);
NSLog(@"%p", [NSObject class]);
2014-11-05 18:48:30.939 Test[11682:865988] 0x7fff768d40f0
2014-11-05 18:48:30.940 Test[11682:865988] 0x7fff768d40f0
繼續開啟objc原始碼,在 Object.mm 中,我們發現 isKindOfClass
的實現如下:
- (BOOL)isKindOf:aClass
{
Class cls;
for (cls = isa; cls; cls = cls->superclass)
if (cls == (Class)aClass)
return YES;
return NO;
}
對著上面Meta Class的圖和實現,我們可以看出
- 當 NSObject Class物件第一次進行比較時,得到它的isa為 NSObject的Meta Class, 這個時候 NSObject Meta Class 和 NSObject Class不相等。
- 然後取NSObject 的Meta Class 的Super class,這個時候又變成了 NSObject Class, 所以返回相等
所以上述第一個輸出結果是 YES 。
我們在看下 ‘isMemberOfClass’的實現:
- (BOOL)isMemberOf:aClass
{
return isa == (Class)aClass;
}
綜上所述,當前的 isa 指向 NSObject 的 Meta Class, 所以和 NSObject Class不相等。
所以上述第二個輸出結果為 NO 。
繼續看後面兩個呼叫:
- Sark Class 的isa指向的是 Sark的Meta Class,和Sark Class不相等
- Sark Meta Class的super class 指向的是 NSObject Meta Class, 和 Sark Class不相等
- NSObject Meta Class的 super class 指向 NSObject Class,和 Sark Class 不相等
- NSObject Class 的super class 指向 nil, 和 Sark Class不相等
所以後面兩個呼叫的結果都輸出為 NO 。
下一篇部落格的主要分享的內容是關於
Objective C Runtime中 訊息和Category 的學習筆記。