Runtime objc4-756.2 isa_t與isa (1)

flag: 一年前寫的runtime源碼博客,今天看objc4已經更新到756.2版本了,接下來一個月內讀完並寫出對應的博客

前情提要:

runtime的源碼版本: objc4-756.2
時間:2019-12-04

Runtime 可能並不是你看到的那樣

在搜索引擎中搜索“Runtime”

baidu:

google runtime

google:

baidu runtime

這裏邊第一頁的內容,挨個點進去,所有的人都會拿以下代碼說事情:

struct objc_object {
    Class isa  OBJC_ISA_AVAILABILITY;
};

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;

但是這裏邊有

#if !__OBJC2__  如果不是Objective-C 2.0
OBJC2_UNAVAILABLE; Objective-C 2.0不可用

那我們現在是什麼版本?Objective-C 2.0啊!!!

2006年7月它就發佈了,雖然研究老的代碼是有參考意義的,但是這麼久遠的實現應該被時間消逝的無影無蹤了吧。有新的爲什麼不看?

通過Opensource我下載了objc4-723版本,也就是截止2018-11-23最新的runtime開源源碼

常用結構體的變化

之前的版本定義:

// ??Class?id
typedef struct objc_class *Class; typedef struct objc_object *id;
// ??????
typedef struct objc_method *Method;
typedef struct objc_ivar *Ivar;
typedef struct objc_category *Category; typedef struct objc_property *objc_property_t;

現在的定義:

typedef struct objc_class *Class; typedef struct objc_object *id;
typedef struct method_t *Method;
typedef struct ivar_t *Ivar;
typedef struct category_t *Category; typedef struct property_t *objc_property_t;

objc_object

// 縮減版代碼,public中都是方法,對結構的研究沒有啥影響
struct objc_object {
private:
    isa_t isa;
}

objc_object問題

1,isa_t是個啥?

1.1 定義

// 縮減版代碼,我們只留下 arm64下的代碼,還有__x86_64__
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 這裏和之前的723版本不同的是換成了宏,但內容本質上沒有變化,可閱讀行提高
    };
#endif
};

// 關於ISA_BITFIELD的定義

# if __arm64__
#   define ISA_MASK        0x0000000ffffffff8ULL
#   define ISA_MAGIC_MASK  0x000003f000000001ULL
#   define ISA_MAGIC_VALUE 0x000001a000000001ULL
#   define ISA_BITFIELD                                                      \
      uintptr_t nonpointer        : 1;                                       \
      uintptr_t has_assoc         : 1;                                       \
      uintptr_t has_cxx_dtor      : 1;                                       \
      uintptr_t shiftcls          : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
      uintptr_t magic             : 6;                                       \
      uintptr_t weakly_referenced : 1;                                       \
      uintptr_t deallocating      : 1;                                       \
      uintptr_t has_sidetable_rc  : 1;                                       \
      uintptr_t extra_rc          : 19
#   define RC_ONE   (1ULL<<45)
#   define RC_HALF  (1ULL<<18)

# elif __x86_64__
#   define ISA_MASK        0x00007ffffffffff8ULL
#   define ISA_MAGIC_MASK  0x001f800000000001ULL
#   define ISA_MAGIC_VALUE 0x001d800000000001ULL
#   define ISA_BITFIELD                                                        \
       
        uintptr_t nonpointer        		: 1; 	// 1:64bit 0:32bit \
        uintptr_t has_assoc         		:1;	 	// 是否有關聯引用或者曾經有關聯引用\
        uintptr_t has_cxx_dtor      		: 1; 	// 是否有析構函數\
        uintptr_t shiftcls         		 	: 33; 	// 所屬類的內存地址,isa的指向的\
        MACH_VM_MAX_ADDRESS 0x1000000000\
        uintptr_t magic             			: 6; 	// 是否初始化完成\
        uintptr_t weakly_referenced 	: 1; 	// 是否被弱引用或者曾經被弱引用\
        uintptr_t deallocating      		: 1; 	// 是否被釋放中\
        uintptr_t has_sidetable_rc  		: 1; 	// 是否引用計數太大超出存儲區域\
        uintptr_t extra_rc          			: 19; 	// 應用計數\
        
#   define RC_ONE   (1ULL<<56)
#   define RC_HALF  (1ULL<<7)

# else
#   error unknown architecture for packed isa
# endif

};

  • 1,這是一個union聯合體,可以在內部寫方法
  • 2,cls/ bits/ isa_t三部分
  • 3,一個描述性聯合體,這是isa_t的核心,用它配合bits可以操作整個對象的內存空間

1.2 作用

  • cls是一個Class類型的,所以通過isa_t->cls 就可以獲取到當前object的類對象,不用擔心消息轉發的方式有什麼問題。

  • bitsuintptr_tunsigned long別名)類型,在64位系統裏,64位,配合一些宏定義做位運算可以方便的獲取到對應的信息,比如:在objc_object中有個 Class ISA()方法,內部實現的核心就是return (Class)(isa.bits & ISA_MASK);

  • ISA_MASK這樣的宏還有很多,結構中的nonpointer has_assoc 等只可以操作獨自負責的內存區域,但是bits可以操作整個對象的內存區域。

  • nonpointer 是對32位處理器的兼容,因爲現在都是64位的,在32位的時代,isa就是一個指針地址.而64位的時代中,變成了isa_t,它是一個聯合體,裏邊包含了64位數據上的對應的位操作.就是elif __x86_64__之下定義的這些參數.nonpointer也在其中.

  • 關於isa在64位處理器就變成了聯合體的原因,是因爲一個指針就佔有64位的空間,8個字節,是非常浪費空間的,蘋果在這個問題上使用了Tagged Pointer的內存處理技術,在後邊我會單獨拿出來寫一篇博客.

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

因爲objc_class是繼承自objc_object的,可以通過ISA()方法獲取到對應的元類地址,這裏也不用擔心原有的消息轉發流程問題。

  • superclass 根據註釋我們可以看到出來它的作用就是原來isa指針的作用,指向父類的指針,注意這裏是類對象的“父類”,最終會一層層指向到根元類,根元類也是一個objc_class對象,他的isa指針指向自己.這樣在整個結構就形成了閉環.
  • cachecache_t類型,用來處理已經調用的方法緩存
  • class_data_bits_t 類型的bits是重點,這裏邊bits和前邊isa_t的作用是一樣的
  • class_rw_t與之對應的還有一個class_ro_t,前者是class在運行時狀態的操作對象,它裏邊會持有對應的class_ro_t,可讀可寫。後者是編譯時生成的class類型,只讀。這兩者的詳細區別下篇文章解釋

常見場景解讀

我們經常看到解釋isa的一個圖

12

objc_object objc_class在objc4源碼中的定義我們已經在上邊解讀了

對應圖中的關係如下:

duiying
這樣我們可以看出isa指向的是這個對象所屬的類,(很多文章說是父類,其實是不準確的).爲什麼這麼說

我們可以看objct.mmsuperclass的定義


+ (Class)superclass {
    return self->superclass;
}

- (Class)superclass {
    return [self class]->superclass;
}

所以這句話是不準確的,對於isa指向的描述用指向本對象所屬類是比較好的.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章