1. 程式人生 > >刨根問底Objective-C Runtime(2)- Object & Class & Meta Class

刨根問底Objective-C Runtime(2)- Object & Class & Meta Class

原文地址:http://chun.tips/blog/2014/11/05/bao-gen-wen-di-objective%5Bnil%5Dc-runtime-(2)%5Bnil%5D-object-and-class-and-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

 在 objc.h 中定義如下:

/// 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 的學習筆記。