Objective-C 中的類

此文實(shí)際成于 2015/07/29

Objc explain Classes and metaclasses

Objective-C 是基于類的對(duì)象系統(tǒng)。

  1. 每一個(gè)對(duì)象都是某一個(gè)類的一個(gè)實(shí)例。

  2. 對(duì)象的 isa 指針指向它的類。

  3. 類描述對(duì)象的數(shù)據(jù)信息如:分配的內(nèi)存大小,ivar類型及布局。

  4. 類同時(shí)描述了對(duì)象的行為如:它所響應(yīng)的選擇器;它所實(shí)現(xiàn)的實(shí)例方法。

  5. 類的方法列表是實(shí)例方法的集合,對(duì)象能夠響應(yīng)選擇器。
    當(dāng)發(fā)送一個(gè)消息給對(duì)象時(shí),objc_msgSend()查詢類(或超類,如果有的話)的方法列表,來決定調(diào)用哪一個(gè)方法。

  6. 每一個(gè) Objective-C 類同樣是一個(gè)對(duì)象。 它有一個(gè)isa指針及其他數(shù)據(jù),
    同樣能響應(yīng)選擇器。當(dāng)你像這樣 [NSObject alloc] 調(diào)用一個(gè)類方法(class method)時(shí)。實(shí)際是你是在發(fā)送消息給哪一個(gè)類對(duì)象。

  7. 因?yàn)橐粋€(gè)類也是一個(gè)對(duì)象,它就必須是某一個(gè)元類(meta class)的實(shí)例.

metaclass 是類對(duì)象的描述。正如類是普通的實(shí)例的描述。
實(shí)際上,

  1. 類的方法列表就是類方法列表,類對(duì)象響應(yīng)的選擇器。
  2. 當(dāng)你發(fā)送一個(gè)消息給一個(gè)類(一個(gè)元類的實(shí)例),objc_msgSend() 查找元類(或其超類,如果有的話)的方法列表來決定調(diào)用哪一個(gè)方法。

正如實(shí)例方法以類來描述以代表類對(duì)象。類方法以元類來描述來代表實(shí)例對(duì)象。

什么是元類?

元類是根類的元類的實(shí)例。 根元類本身是根根元類的一個(gè)實(shí)例。isa鏈在這里以一個(gè)循環(huán)結(jié)尾: 實(shí)例到類對(duì)元類到根類到它自身。元類的 isa指針的行為一般不影響什么的。因?yàn)樵诂F(xiàn)實(shí)中沒有人發(fā)送信息給元類對(duì)象。

最重要的是一個(gè)元類的超類。元類的超類鏈跟類的越類鏈?zhǔn)遣⑿械?,所以類方法的繼承也跟實(shí)例方法的繼承是并行的。并且根元類的越類是根類,因此每一個(gè)類對(duì)象響應(yīng)根類的實(shí)例方法。
最后,類對(duì)象是根類的或其子類的實(shí)例,正如其他對(duì)象一個(gè)。

如下圖:


objc class and meta class

對(duì)應(yīng)代碼如下:

    // Connect to superclasses and metaclasses
    cls->initClassIsa(meta);
    if (superclass) {
        meta->initClassIsa(superclass->ISA()->ISA());
        cls->superclass = superclass;
        meta->superclass = superclass->ISA();
        addSubclass(superclass, cls);
        addSubclass(superclass->ISA(), meta);
    } else {
        meta->initClassIsa(meta);
        cls->superclass = Nil;
        meta->superclass = cls;
        addSubclass(cls, meta);
    }

上面的 對(duì)于isa的設(shè)置主要在 cls->initClassIsa(meta) 方法中體現(xiàn) 。

inline void 
objc_object::initClassIsa(Class cls)
{
    initIsa(cls);
}

然后很多與初始化isa操作,最后都調(diào)用到了initIsa(cls) 方法上來。

對(duì)于非 Tagged Pointer 的 ISA 類型,
其初始 化操作就是一個(gè)簡單的賦值操作:

inline void 
objc_object::initIsa(Class cls)
{
    assert(!isTaggedPointer()); 
    isa = (uintptr_t)cls; 
}

對(duì)于 Tagged Pointer 實(shí)現(xiàn)的

其實(shí)現(xiàn)稍微復(fù)雜一些,如下 :


inline void 
objc_object::initIsa(Class cls)
{
    initIsa(cls, false, false);
}

inline void 
objc_object::initClassIsa(Class cls)
{
    if (DisableIndexedIsa) {
        initIsa(cls, false, false);
    } else {
        initIsa(cls, true, false);
    }
}

inline void
objc_object::initProtocolIsa(Class cls)
{
    return initClassIsa(cls);
}

inline void 
objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
    assert(!UseGC);
    assert(!cls->requiresRawIsa());
    assert(hasCxxDtor == cls->hasCxxDtor());

    initIsa(cls, true, hasCxxDtor);
}

inline void 
objc_object::initIsa(Class cls, bool indexed, bool hasCxxDtor) 
{ 
    assert(!isTaggedPointer()); 
    
    if (!indexed) {
        isa.cls = cls;
    } else {
        assert(!DisableIndexedIsa);
        isa.bits = ISA_MAGIC_VALUE;
        // isa.magic is part of ISA_MAGIC_VALUE
        // isa.indexed is part of ISA_MAGIC_VALUE
        isa.has_cxx_dtor = hasCxxDtor;
        isa.shiftcls = (uintptr_t)cls >> 3;
    }
}


小結(jié)

當(dāng)像一個(gè)對(duì)象發(fā)送消息時(shí),方法的查找以對(duì)象的isa指針開始,然后繼續(xù)到其超類鏈。
實(shí)例方法在類中定義
類方法在元類定義加上根類(非元類)。

類方法與實(shí)例方法的查找

按上面的說法,類方法其實(shí)是類對(duì)象isa所指向的元類的實(shí)例方法。
因此其實(shí)現(xiàn)如下 :

/***********************************************************************
* class_getClassMethod.  Return the class method for the specified
* class and selector.
**********************************************************************/
Method class_getClassMethod(Class cls, SEL sel)
{
    if (!cls  ||  !sel) return nil;

    return class_getInstanceMethod(cls->getMeta(), sel);
}

新類的內(nèi)存分配

/***********************************************************************
* objc_allocateClassPair
* fixme
* Locking: acquires runtimeLock
**********************************************************************/
Class objc_allocateClassPair(Class superclass, const char *name, 
                             size_t extraBytes)
{
    Class cls, meta;

    rwlock_write(&runtimeLock);

    // Fail if the class name is in use.
    // Fail if the superclass isn't kosher.
    if (getClass(name)  ||  !verifySuperclass(superclass, true/*rootOK*/)) {
        rwlock_unlock_write(&runtimeLock);
        return nil;
    }

    // Allocate new classes.
    cls  = alloc_class_for_subclass(superclass, extraBytes);
    meta = alloc_class_for_subclass(superclass, extraBytes);

    // fixme mangle the name if it looks swift-y?
    objc_initializeClassPair_internal(superclass, name, cls, meta);

    rwlock_unlock_write(&runtimeLock);

    return cls;
}

    cls  = alloc_class_for_subclass(superclass, extraBytes);
    meta = alloc_class_for_subclass(superclass, extraBytes);

可以看出,類和元類的繼承是并行的。

整個(gè)類復(fù)雜的關(guān)聯(lián)就在下面這大段代碼中體現(xiàn):

/***********************************************************************
* objc_initializeClassPair
* Locking: runtimeLock must be write-locked by the caller
**********************************************************************/

// &UnsetLayout is the default ivar layout during class construction
static const uint8_t UnsetLayout = 0;

static void objc_initializeClassPair_internal(Class superclass, const char *name, Class cls, Class meta)
{
    rwlock_assert_writing(&runtimeLock);

    class_ro_t *cls_ro_w, *meta_ro_w;

    cls->cache.setEmpty();
    meta->cache.setEmpty();
    
    cls->setData((class_rw_t *)_calloc_internal(sizeof(class_rw_t), 1));
    meta->setData((class_rw_t *)_calloc_internal(sizeof(class_rw_t), 1));
    cls_ro_w   = (class_ro_t *)_calloc_internal(sizeof(class_ro_t), 1);
    meta_ro_w  = (class_ro_t *)_calloc_internal(sizeof(class_ro_t), 1);
    cls->data()->ro = cls_ro_w;
    meta->data()->ro = meta_ro_w;

    // Set basic info

    cls->data()->flags = RW_CONSTRUCTING | RW_COPIED_RO | RW_REALIZED | RW_REALIZING;
    meta->data()->flags = RW_CONSTRUCTING | RW_COPIED_RO | RW_REALIZED | RW_REALIZING;
    cls->data()->version = 0;
    meta->data()->version = 7;

    cls_ro_w->flags = 0;
    meta_ro_w->flags = RO_META;
    if (!superclass) {
        cls_ro_w->flags |= RO_ROOT;
        meta_ro_w->flags |= RO_ROOT;
    }
    if (superclass) {
        cls_ro_w->instanceStart = superclass->unalignedInstanceSize();
        meta_ro_w->instanceStart = superclass->ISA()->unalignedInstanceSize();
        cls->setInstanceSize(cls_ro_w->instanceStart);
        meta->setInstanceSize(meta_ro_w->instanceStart);
    } else {
        cls_ro_w->instanceStart = 0;
        meta_ro_w->instanceStart = (uint32_t)sizeof(objc_class);
        cls->setInstanceSize((uint32_t)sizeof(id));  // just an isa
        meta->setInstanceSize(meta_ro_w->instanceStart);
    }

    cls_ro_w->name = _strdup_internal(name);
    meta_ro_w->name = _strdup_internal(name);

    cls_ro_w->ivarLayout = &UnsetLayout;
    cls_ro_w->weakIvarLayout = &UnsetLayout;

    // Connect to superclasses and metaclasses
    cls->initClassIsa(meta);
    if (superclass) {
        meta->initClassIsa(superclass->ISA()->ISA());
        cls->superclass = superclass;
        meta->superclass = superclass->ISA();
        addSubclass(superclass, cls);
        addSubclass(superclass->ISA(), meta);
    } else {
        meta->initClassIsa(meta);
        cls->superclass = Nil;
        meta->superclass = cls;
        addSubclass(cls, meta);
    }
}


Objective-C 的類是以 struct objc_class來實(shí)現(xiàn)的。
而其繼承自 struct objc_objectid

struct objc_class有新舊兩種定義。老的定義雖然很 OS X 10.5 就不再用了。但是有助于幫助我們理解 struct objc_class有什么 。

類的老的運(yùn)行時(shí)定義

老的定義在 objc-runtime-old.h中。

struct objc_class : objc_object {
    Class superclass;
    const char *name;
    uint32_t version;
    uint32_t info;
    uint32_t instance_size;
    struct old_ivar_list *ivars;
    struct old_method_list **methodLists;
    Cache cache;
    struct old_protocol_list *protocols;
    // CLS_EXT only
    const uint8_t *ivar_layout;
    struct old_class_ext *ext;

上面的 Class類型,是:
typedef struct objc_class *Class
它們都在 objc-private.h中有定義:

struct objc_class;
struct objc_object;

typedef struct objc_class *Class;
typedef struct objc_object *id;

也就是說objc_class實(shí)現(xiàn)了一種單鏈表的結(jié)構(gòu)。也可以說它是類的單繼承結(jié)構(gòu)。
Class好比是鏈表結(jié)構(gòu)中的一個(gè)Node

從上面的結(jié)構(gòu)定義可以看出老式的類定義有哪些內(nèi)容呢?

- `Class superclass` 其指向的超類
- `const char *name` 類名
- `version` 類的版本
- `info` 類信息
- `instance_size` 類對(duì)象實(shí)例的大小
- `struct objc_ivar_list *ivars` 實(shí)例變量列表
- `struct objc_method_list **methodLists` 實(shí)例方法列表
- `struct objc_cache *cache` 方法解析緩存的Hash 表
- `struct objc_protocol_list *protocols` 類實(shí)例實(shí)現(xiàn)的協(xié)議列表

也不能忘了其公開的頭文件的聲明 `runtime.h`
中將`isa`字段也包括在內(nèi)。但是實(shí)現(xiàn)是在`struct objc_object`中聲明的。

```c
struct objc_class {
Class isa  OBJC_ISA_AVAILABILITY;
}
```

`isa` 表明一個(gè)實(shí)際的對(duì)象實(shí)際上是一個(gè)什么類。實(shí)際上每一個(gè) objc 的對(duì)象都 是一個(gè)一個(gè)`isa`指針開始的,要不 objc 運(yùn)行時(shí)不知道怎么處理這一塊內(nèi)存,這一個(gè)對(duì)象。

新的類定義

其基本聲明如下 :

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
    class_rw_t *data() { 
        return bits.data();
     }
 }

新的實(shí)現(xiàn)更復(fù)雜高效。

上面介紹的實(shí)例變量列表,方法列表,協(xié)議列表都由 class_rw_t結(jié)構(gòu)來實(shí)現(xiàn)了。

其聲明如下 :

struct class_rw_t {
    uint32_t flags;
    uint32_t version;

    const class_ro_t *ro;

    union {
        method_list_t **method_lists;  // RW_METHOD_ARRAY == 1
        method_list_t *method_list;    // RW_METHOD_ARRAY == 0
    };
    struct chained_property_list *properties;
    const protocol_list_t ** protocols;

    Class firstSubclass;
    Class nextSiblingClass;

    char *demangledName;

其中 const class_ro_t *ro; 存儲(chǔ)了方法列表,協(xié)議列表,實(shí)例變量列表的關(guān)鍵信息。

struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
#ifdef __LP64__
    uint32_t reserved;
#endif

    const uint8_t * ivarLayout;
    
    const char * name;
    const method_list_t * baseMethods;
    const protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;

    const uint8_t * weakIvarLayout;
    const property_list_t *baseProperties;
};

參考文檔

  1. Friday Q&A 2009-03-13: Intro to the Objective-C Runtime
  2. [objc explain] Classes and metaclasses
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 你真的知道OC的類(Class)是個(gè)什么玩意兒? 眾所周知,所有的對(duì)象都是由其對(duì)應(yīng)的類實(shí)例化而來,但是類本身其實(shí)也...
    潭清閱讀 596評(píng)論 0 0
  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 2,055評(píng)論 0 9
  • 原文出處:南峰子的技術(shù)博客 Objective-C語言是一門動(dòng)態(tài)語言,它將很多靜態(tài)語言在編譯和鏈接時(shí)期做的事放到了...
    _燴面_閱讀 1,419評(píng)論 1 5
  • 我們都知道,Objective-C是一門基于C語言的面向?qū)ο蟮恼Z言,它的對(duì)象模型是基于類來建立的。我們可以在蘋果開...
    Tamp_閱讀 1,603評(píng)論 1 13
  • 今天是假期的最后一天,晨起有點(diǎn)著涼,迷迷糊糊地睡了一上午。臨近11點(diǎn),被老公喊起,他炒菜,我洗衣。午飯是母親早已煮...
    花兒朵朵開7閱讀 523評(píng)論 1 5

友情鏈接更多精彩內(nèi)容