ObjC runtime源碼(二):isa_t, objc_class(類), objc_object(對象)結構體

經過這篇文章,你會更深刻的瞭解isa_t結構體與ObjC的類,對象之間的關係,並從源碼中來了解isKindOfClass和isMemberOfClass的邏輯並能區分isKindOfClass和isMemberOfClass的區別git

可編譯的runtime源碼(objc4-706)github

上一篇文章經過ObjC的runtime源碼簡單講了下ObjC裏的類跟類族,經過源碼咱們發現ObjC中的類也一樣是一個對象,也簡單的瞭解了isa_t, objc_class, objc_object的結構,這篇文章會詳細的講解下這三個結構體,經過這篇文章,你會更深刻的瞭解ObjC的類與對象之間的關係bash


對象與類的關係

對象是指某個類的實例,對象是一個objc_object的結構體,類是一個objc_class的結構體ui


ObjC對象類與對象實例的描述

咱們都知道ObjC全部的對象對應的類都是NSObject,那麼想要找到對象類的描述,就要從NSObject開始一步一步往下找es5

NSObject.h中:spa

#include <objc/objc.h>

@interface NSObject <NSObject> {
    Class isa OBJC_ISA_AVAILABILITY;
}
複製代碼

NSObject中包含一個Class類型的isa。那麼這個Class是什麼?指針

objc.h中咱們能夠找到這個Classcode

/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;
複製代碼

因此這個Class是一個指向objc_class結構體的指針orm

那麼這個objc_class又是什麼?繼續往下找,在runtime.h中咱們能夠找到這個objc_class,可是前提是#if !OBJC_TYPES_DEFINEDOBJC_TYPES_DEFINEDobjc-private.h中被定義#define OBJC_TYPES_DEFINED 1,因此runtime.h中的objc_class不會被編譯,真正的objc_class的定義是在objc-private.hcdn

struct objc_class;

typedef struct objc_class *Class;
複製代碼

繼續往下找,最終在objc-runtime-new.h中,咱們找到了objc_class的定義

struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags
    ...
}
複製代碼

objc_class是一個表明對象類的結構體,因此NSObject類中包含的是一個objc_class結構體類型的isa,指向該對象對應的類。

objc_runtime_1.png

objc_class是繼承於objc_object,在objc-private.h中,咱們能夠找到objc_object的定義

struct objc_object {
private:
    isa_t isa;
}
複製代碼

objc_object是一個表明對象實例的結構體,objc_object中只包含一個isa_t結構體類型的isa,因此objc_class中也會包含一個isa_t結構體類型的isa這個isa_t結構體包含了當前對象指向的類的信息。

因此總結下來就是NSObject包含了一個objc_class結構體類型的isaobjc_class繼承於objc_object,因此NSObject還會包含一個isa_t結構體的isa,以下圖:

objc_runtime_2.png

NSObject的一個實例對象objectNSObject這個類的關係以下:

objc_runtime_3-4.png


isa_t結構體

簡單來講objc_object中的isa_t這個結構體包含了了當前對象指向的類的信息,因此NSObject的一個實例對象object包含的isa指向了NSObject這個類,NSObject包含的isa指向其元類(MetaClass),關於元類請看上一篇中的解釋。

Runtime-2.png


isKindOfClass和isMemberOfClass的區別

  • isKindOfClass給出的定義是: Returns a Boolean value that indicates whether the receiver is an instance of given class or an instance of any class that inherits from that class
  • isMemberOfClass給出的定義是: Returns a Boolean value that indicates whether the receiver is an instance of a given class

大概的就是說isKindOfClass不但會判斷接受者是否爲該Class的實例,還會判斷接受者是否爲該Class的任何繼承類的實例,而isMemberOfClass只會判斷接受者是否爲該Class的實例。

咱們在NSObject.mm中能夠找到isKindOfClassisMemberOfClass的源碼:

// isKindOfClass
+ (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = object_getClass((id)self); tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}

- (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}

// isMemberOfClass
+ (BOOL)isMemberOfClass:(Class)cls {
    return object_getClass((id)self) == cls;
}

- (BOOL)isMemberOfClass:(Class)cls {
    return [self class] == cls;
}
複製代碼

咱們經過下面這個例子來走一下源碼流程

Father *father = [[Father alloc] init];
        
BOOL res1 = [father isKindOfClass:[NSObject class]];
BOOL res2 = [father isMemberOfClass:[NSObject class]];
BOOL res3 = [[NSObject class] isKindOfClass:[NSObject class]];
BOOL res4 = [[NSObject class] isMemberOfClass:[NSObject class]];
BOOL res5 = [[Father class] isKindOfClass:[Father class]];

輸出爲
res1: YES
res2: NO
res3: YES
res4: NO
res5: NO
複製代碼
  • res1中接受者是Father類的一個實例對象,因此會走到- (BOOL)isKindOfClass:(Class)cls方法中

    1. Class tcls = [self class] => tcls = Father
    2. tcls => YES
    3. if (tcls == cls) => Father != NSObject
    4. tcls = tcls->superclass => tcls = NSObject
    5. tcls => YES
    6. if (tcls == cls) => NSObject == NSObject, 返回YES
  • res2中接受者是Father類的一個實例對象,因此會走到- (BOOL)isMemberOfClass:(Class)cls方法中

    1. [self class] == cls => Father != NSObject, 返回NO
  • res3中接受者是NSObject本身([NSObject class]返回self),因此會走到+ (BOOL)isKindOfClass:(Class)cls方法中

    1. Class tcls = object_getClass((id)self) => tcls = MetaClass of NSObject
    2. tcls => YES
    3. if (tcls == cls) => MetaClass of NSObject != NSObject
    4. tcls = tcls->superclass => MetaClass of NSObject->superclass, tcls = NSObject, MetaClass of NSObject爲RootClass(meta),因此該類的superclass返回RootClass,即NSObject,詳見第一篇runtime中的類關係圖
    5. tcls => YES
    6. if (tcls == cls) => NSObject == NSObject, 返回YES
  • res4中接受者是NSObject本身([NSObject class]返回self),因此會走到+ (BOOL)isMemberOfClass:(Class)cls方法中

    1. object_getClass((id)self) == cls => MetaClass of NSObject != NSObject,返回NO
  • res5中接受者是Father本身([Father class]返回self),因此會走到+ (BOOL)isKindOfClass:(Class)cls方法中

    1. Class tcls = object_getClass((id)self) => tcls = MetaClass of Father
    2. tcls => YES
    3. if (tcls == cls) => MetaClass of Father != Father
    4. tcls = tcls->superclass => MetaClass of Father->superclass, tcls = MetaClass of NSObject
    5. tcls => YES
    6. if (tcls == cls) => MetaClass of NSObject != Father
    7. tcls = tcls->superclass => MetaClass of NSObject->superclass, tcls = NSObject
    8. tcls => YES
    9. if (tcls == cls) => NSObject != Father
    10. tcls = tcls->superclass => NSObject->superclass, tcls = nil
    11. tcls => NO, 返回NO
  • isKindOfClass總結下來就是判斷接受者的isa指向類是否是屬於某個類或者屬於某個類的繼承類

類關係圖

objc-isa-class-diagram.jpg


總結

總結下來,ObjC中的實例對象是一個objc_object的結構體,該結構體中包含了一個isa_t結構體的isa,該isa_t結構體包含了當前對象指向的類的信息。ObjC中的類是一個objc_class的結構體,該結構體繼承於objc_object,因此ObjC中的類也是一個對象,類對象中的isa_t結構體包含了類對象指向的元類(meta-class)的信息。因此ObjC中實例對象和類對象的關係就是一個isa指向鏈的關係,經過object_getClass和superclass方法能夠獲取該指向鏈。object_getClass會返回接受者的isa指向者,superclass會返回接受者的父類。isKindOfClassisMemberOfClass中使用到了object_getClass的方法來獲取指向鏈,isKindOfClass會經過for循環接受者的isa指向鏈來判斷接受者是否是某個類的實例或者是某個類的繼承類的實例。下篇文章會繼續經過分析runtime源碼來講說ObjC中的方法結構和消息發送。

轉載請註明出處,原文地址:http://www.kobedai.me/objc-runtime_2/

相關文章
相關標籤/搜索