《OC底層系列三》-對象和類的關(guān)聯(lián)

前言

我們都知道對象由類實例化而來,在上一篇《OC底層系列二》-對象中,我們知道對象是一個objc_object類型的結(jié)構(gòu)體,類是一個objc_class類型的結(jié)構(gòu)體,今天我們從底層來探究對象和類是如何建立關(guān)聯(lián)。

目錄

image.png

0、簡介

  • 本文主要探索OC對象和類之間如何建立關(guān)聯(lián),并進(jìn)行驗證。

1、對象和類的定義

  • 我們再從源碼角度看看對象和類的定義
typedef struct objc_class *Class; // 類的定義
typedef struct objc_object *id;  // 對象的定義

1.1、objc_object

// objc-private.h

// objc_object 的實現(xiàn)
struct objc_object {
private:
    isa_t isa;
以下是一些函數(shù),省略
...
}

1.2、objc_class

// objc-runtime-new.h

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();
    } 
// objc_class的部分方法代碼省略不貼出來
}
  • 從源碼中我們看到objc_class繼承自objc_object,說明了類也是一個對象objc_class繼承自objc_object,自然也就繼承了objc_object的成員,則objc_class也擁有一個isa,由此我們推斷對象和類的關(guān)聯(lián)在于isa。

2、isa

// objc-private.h

// isa_t的定義,一個聯(lián)合體,值為cls或者bits
union isa_t {
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }

    Class cls;
    uintptr_t bits;
#if defined(ISA_BITFIELD)
    struct {
        ISA_BITFIELD;  // defined in isa.h
    };
#endif
};

2.1、isa的初始化

  • objc_object中初始化isa方法源碼如下,如下:
// objc_private.h

struct objc_object {
private:
    isa_t isa;

public:  
···
private:
    void initIsa(Class newCls, bool nonpointer, bool hasCxxDtor);
···
}
// objc-object.h

inline void 
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) 
{ 
    assert(!isTaggedPointer()); 
    
    if (!nonpointer) {
// 2.1.1
        isa.cls = cls;
    } else {
// 2.1.2
        assert(!DisableNonpointerIsa);
        assert(!cls->instancesRequireRawIsa());

        isa_t newisa(0);

#if SUPPORT_INDEXED_ISA
// 2.1.3
        assert(cls->classArrayIndex() > 0);
        newisa.bits = ISA_INDEX_MAGIC_VALUE;
        // isa.magic is part of ISA_MAGIC_VALUE
        // isa.nonpointer is part of ISA_MAGIC_VALUE
        newisa.has_cxx_dtor = hasCxxDtor;
        newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#else
// 2.1.4
        newisa.bits = ISA_MAGIC_VALUE;
        // isa.magic is part of ISA_MAGIC_VALUE
        // isa.nonpointer is part of ISA_MAGIC_VALUE
        newisa.has_cxx_dtor = hasCxxDtor;
        newisa.shiftcls = (uintptr_t)cls >> 3;
#endif

        // This write must be performed in a single store in some cases
        // (for example when realizing a class because other threads
        // may simultaneously try to use the class).
        // fixme use atomics here to guarantee single-store and to
        // guarantee memory order w.r.t. the class index table
        // ...but not too atomic because we don't want to hurt instantiation
        isa = newisa;
    }
}
  • 通常我們創(chuàng)建的OC對象都是nonpointer,所以會執(zhí)行2.1.2,創(chuàng)建一個isa_t類型的newisa并初始化其成員bits為0。
  • SUPPORT_INDEXED_ISA定義如下,根據(jù)注釋,其表示 isa_t 中存放的 Class 表示的是否為Class 的地址,是一個索引(根據(jù)該索引可在類信息表中查找該類結(jié)構(gòu)地址)。
// objc-config.h

// Define SUPPORT_INDEXED_ISA=1 on platforms that store the class in the isa 
// field as an index into a class table.
// Note, keep this in sync with any .s files which also define it.
// Be sure to edit objc-abi.h as well.
#if __ARM_ARCH_7K__ >= 2  ||  (__arm64__ && !__LP64__)
#   define SUPPORT_INDEXED_ISA 1
#else
#   define SUPPORT_INDEXED_ISA 0
#endif
  • 我們來分析一下SUPPORT_INDEXED_ISA宏定義的值,__ARM_ARCH_7K__,從名稱上看其表示在 ARM 7k 架構(gòu) CPU 的代碼中的標(biāo)志宏,我們可以在LLVM 開源的ARM.cpp找到其定義:
// ARM.cpp

// Unfortunately, __ARM_ARCH_7K__ is now more of an ABI descriptor. The CPU
// happens to be Cortex-A7 though, so it should still get __ARM_ARCH_7A__.
if (getTriple().isWatchABI())
  Builder.defineMacro("__ARM_ARCH_7K__", "2");
  • 通過注釋和代碼可以知道,這個值只有在iWatch下才會被定為2,換句話說__ARM_ARCH_7K__代表的是iWatch的宏。由此可知,在iPhone模擬器以及真機上__ARM_ARCH_7K__值為FALSE

  • __arm64__表示是否采用arm64架構(gòu)。在模擬器上為FALSE,真機上為TRUE。

  • __LP64__ 表示在64位機器上,如果int是32位,long是64位,pointer也是64位,那么該機器就是__LP64__。在模擬器上和真機上__LP64__TRUE。

  • 綜上,ARM_ARCH_7K >= 2 || (arm64 && !LP64)
    =FALSE || FALSE
    =FALSE

    所以SUPPORT_INDEXED_ISA的值為FALSE

  • 因此初始化isa方法走的是2.1.4

// 2.1.4
 newisa.bits = ISA_MAGIC_VALUE;
// isa.magic is part of ISA_MAGIC_VALUE
// isa.nonpointer is part of ISA_MAGIC_VALUE
newisa.has_cxx_dtor = hasCxxDtor;
newisa.shiftcls = (uintptr_t)cls >> 3;

2.2、shiftcls

shiftcls的值是isa中cls的值右移三位得到,shiftcls存儲類指針的值。根據(jù)shiftcls的宏定義,在 x86_64 架構(gòu)有 44位 用來存儲類指針,arm64 架構(gòu)中有 33位 。

  • 我們在x86_64(模擬器下)為研究,ISA_BITFIELD定義如下:


    image.png
  • shiftcls的值為二進(jìn)制的3~46位。

  • 我們通過LLDB驗證shitfcls是否存儲著cls信息,添加如下代碼并在NSLog處添加斷點:

#import <Foundation/Foundation.h>
// Animal繼承NSObject,沒有添加任何成員
#import "Animal.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        Animal *animal = [Animal alloc];
        NSLog(@"Hello, World!");
    }
    return 0;
}
  • 在控制臺使用LLDB如下操作:


    image.png
  • x/4gx 打印對象anmial的內(nèi)存信息,以16進(jìn)制格式打印4個8字節(jié)。其第一個8字節(jié)為isa。
  • p/t animal 將[animal class]的值以二進(jìn)制格式輸出。
格式化二進(jìn)制如下:
// [animal class]
$0 = 0b 0000 0000 0000 0000 00   00 0000 0000 0001 0000 0000 0000 0000 0010 0000 1111 0   000
// isa
$2 = 0b 0000 0000 0001 1101 10   00 0000 0000 0001 0000 0000 0000 0000 0010 0000 1111 0   001
  • 對上面打印結(jié)果格式化可以看到,[animal class]3~46位isa3~46位位是一樣的,驗證了對象的關(guān)于類的信息是存在在isa中的shiftcls中。

3、[object class]方法分析

  • [animal class]中class的底層實現(xiàn):
\\ objc-object.h
inline Class 
objc_object::ISA() 
{
    ASSERT(!isTaggedPointer()); 
#if SUPPORT_INDEXED_ISA
// 3.1
    if (isa.nonpointer) {
        uintptr_t slot = isa.indexcls;
        return classForIndex((unsigned)slot);
    }
    return (Class)isa.bits;
#else
// 3.2
    return (Class)(isa.bits & ISA_MASK);
#endif
}
  • SUPPORT_INDEXED_ISA值為FALSE,所以走//3.2,返回(Class)(isa.bits & ISA_MASK);
// isa.h

define ISA_MASK        0x00007ffffffffff8ULL

4、通過LLDB驗證對象和類如何關(guān)聯(lián)

  • 我們根據(jù)isa.bits & ISA_MASK使用LLDB進(jìn)行驗證,繼續(xù)如下操作

    image.png

  • $3即ISA_MASK的二進(jìn)制表示,$6即(isa.bits &ISA_MASK)的結(jié)果,最后我們d打印$6的描述為Animal,驗證完畢。

5、總結(jié)

  • 對象的類信息存儲在其isa中的的shiftcls中,對象和類的通過對象中的ias的shiftcls進(jìn)行關(guān)聯(lián),class方法本質(zhì)上返回的是isa的shiftcls,其底層實現(xiàn)通過isa.bit& ISA_MASK獲取到shiftcls的值。
  • 我們可以理解為類實例化的對象的isa指向該類。

6、TODO

  • OC對象的isa(其位域成員shiftcls)中的存著類的信息,OC類也是一個對象,那么OC類的isa(其位域成員shiftcls)存儲著什么呢?
  • 源碼中objc-class有一個superclass,我們可以推測superclass代表當(dāng)前類的父類,Animalsuperclass應(yīng)該為NSObject類,那么NSObjec類superclass是什么?
  • 這些我會在下一篇文章中繼續(xù)探究。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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