iOS底层原理 04 : isa指向&类的结构分析

2020-09-14  本文已影响0人  smooth_lgh

前言:
1.我们前面已经对isa的结构进行了分析,但整个isa指向过程是什么呢?
2.我们经常看到的那张经典的isa走位图又该如何理解呢?
3.前面我们已经知道实例对象的isa指针指向了存储类信息的类对象,那么这个存储类信息的类的结构又是怎样的呢?

使用LLDB探索isa走位

(lldb) po p
<LGHPerson: 0x1006d1dc0>

(lldb) x/4gx  0x1006d1dc0
0x1006d1dc0: 0x001d800100002181 0x0000000000000000
0x1006d1dd0: 0x726573554b575b2d 0x7320747069726353
(lldb) p/x  0x001d800100002181 & 0x00007ffffffffff8ULL
(unsigned long long) $3 = 0x0000000100002180
(lldb) x  0x0000000100002180
0x100002180: 58 21 00 00 01 00 00 00 40 11 3f 00 01 00 00 00  X!......@.?.....
0x100002190: 30 57 6d 00 01 00 00 00 07 00 00 00 10 80 04 00  0Wm.............
(lldb) x LGHPerson.class
0x100002180: 58 21 00 00 01 00 00 00 40 11 3f 00 01 00 00 00  X!......@.?.....
0x100002190: 30 57 6d 00 01 00 00 00 07 00 00 00 10 80 04 00  0Wm.............
(lldb) x  object_getClass(p)
0x100002180: 58 21 00 00 01 00 00 00 40 11 3f 00 01 00 00 00  X!......@.?.....
0x100002190: 30 57 6d 00 01 00 00 00 07 00 00 00 10 80 04 00  0Wm.............
(lldb) x/4gx 0x0000000100002180
0x100002180: 0x0000000100002158 0x00000001003f1140
0x100002190: 0x00000001006d5730 0x0004801000000007
(lldb) p/x 0x0000000100002158 & 0x00007ffffffffff8ULL
(unsigned long long) $8 = 0x0000000100002158
(lldb) x/4gx 0x0000000100002158
0x100002158: 0x00000001003f10f0 0x00000001003f10f0
0x100002168: 0x0000000101208fc0 0x0001e03100000007
(lldb) p/x 0x00000001003f10f0 & 0x00007ffffffffff8ULL
(unsigned long long) $13 = 0x00000001003f10f0
(lldb) x/4gx 0x00000001003f10f0
0x1003f10f0: 0x00000001003f10f0 0x00000001003f1140
0x1003f1100: 0x000000010070a2f0 0x0005e03100000007
(lldb) x/4gx NSObject.class
0x1003f1140: 0x00000001003f10f0 0x0000000000000000
0x1003f1150: 0x000000010110b1c0 0x0001801000000003
(lldb) p/x  0x00000001003f10f0 & 0x00007ffffffffff8ULL
(unsigned long long) $16 = 0x00000001003f10f0
(lldb) x/4gx  0x00000001003f10f0
0x1003f10f0: 0x00000001003f10f0 0x00000001003f1140
0x1003f1100: 0x000000010070a2f0 0x0005e03100000007

总结:
1.实例对象的isa -->类对象
2.类对象的isa指向元类对象
3.元类对象的isa指向根元类对象
4.根元类对象的isa指向自身

LGHPerson的isa走位图.png

同理LGHTeacher的走位图


LGHTeacher的走位图.png NSObject的走位图.png 继承与isa走位图.png

存储类信息的类结构

我们看到objc_class定义,但我们发现OBJC2_UNAVAILABLE,这是已经废弃掉的结构。

// 旧的类结构:
struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
    Class _Nullable super_class                              OBJC2_UNAVAILABLE;
    const char * _Nonnull name                               OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
    struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;

我们继续寻找objc_class的新结构,我们可以看到objc_class的新版定义(objc4-781版本)如下,有以下几个属性

// 在最新的objc源码里面:
struct objc_class : objc_object {
    // Class ISA; //8字节
    Class superclass; //Class 类型 8字节
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags    
    //....方法部分省略,未贴出
}

计算 cache_t 结构体的内存大小

struct cache_t {
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED
    explicit_atomic<struct bucket_t *> _buckets; // 是一个结构体指针类型,占8字节
    explicit_atomic<mask_t> _mask; //是mask_t 类型,而 mask_t 是 unsigned int 的别名,占4字节
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
    explicit_atomic<uintptr_t> _maskAndBuckets; //是指针,占8字节
    mask_t _mask_unused; //是mask_t 类型,而 mask_t 是 uint32_t 类型定义的别名,占4字节
    
#if __LP64__
    uint16_t _flags;  //是uint16_t类型,uint16_t是 unsigned short 的别名,占 2个字节
#endif
    uint16_t _occupied; //是uint16_t类型,uint16_t是 unsigned short 的别名,占 2个字节

总结:所以最后计算出cache类的内存大小 = 12 + 2 + 2 = 16字节

获取bits

所以有上述计算可知,想要获取bits的中的内容,只需通过类的首地址平移32字节即可。如下:($1+32)

(lldb) p/x LGHPerson.class
(Class) $0 = 0x00000001000022a0 LGHPerson
(lldb) p/x 0x00000001000022a0
(long) $1 = 0x00000001000022a0
(lldb) p/x (class_data_bits_t *)($1+32)
(class_data_bits_t *) $2 = 0x00000001000022c0

接下来获取properties(),得到里面存储了name属性

(lldb) p $4.properties()
(const property_array_t) $12 = {
  list_array_tt<property_t, property_list_t> = {
     = {
      list = 0x00000001000021a8
      arrayAndFlag = 4294975912
    }
  }
}
(lldb) p $12.list
(property_list_t *const) $13 = 0x00000001000021a8
(lldb) p *$13
(property_list_t) $14 = {
  entsize_list_tt<property_t, property_list_t, 0> = {
    entsizeAndFlags = 16
    count = 1
    first = (name = "name", attributes = "T@\"NSString\",&,N,V_name")
  }
}
(lldb) p $14.get(0)
(property_t) $15 = (name = "name", attributes = "T@\"NSString\",&,N,V_name")

数组越界,说明只存储了name

(lldb) p $14.get(1)
Assertion failed: (i < count), function get, file /Users/xiaofeiguan/Desktop/04-isa指向&类初探/03-自己补充/可编译objc源码/runtime/objc-runtime-new.h, line 438.
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.

类信息里面的properties()里面只存储属性,没有存储成员变量

接下来获取methods() ,得到-sayHello -setName -name 这个实例方法

(lldb) p $4.methods()
(const method_array_t) $5 = {
  list_array_tt<method_t, method_list_t> = {
     = {
      list = 0x00000001000020d8
      arrayAndFlag = 4294975704
    }
  }
}
(lldb) p $5.list
(method_list_t *const) $6 = 0x00000001000020d8
(lldb) p *$6
(method_list_t) $7 = {
  entsize_list_tt<method_t, method_list_t, 3> = {
    entsizeAndFlags = 26
    count = 4
    first = {
      name = "sayHello"
      types = 0x0000000100000f42 "v16@0:8"
      imp = 0x0000000100000d70 (KCObjc`-[LGHPerson sayHello] at main.m:21)
    }
  }
}
(lldb) p $7.get(0)
(method_t) $8 = {
  name = "sayHello"
  types = 0x0000000100000f42 "v16@0:8"
  imp = 0x0000000100000d70 (KCObjc`-[LGHPerson sayHello] at main.m:21)
}
(lldb) p $7.get(1)
(method_t) $9 = {
  name = ".cxx_destruct"
  types = 0x0000000100000f42 "v16@0:8"
  imp = 0x0000000100000d80 (KCObjc`-[LGHPerson .cxx_destruct] at main.m:20)
}
(lldb) p $7.get(2)
(method_t) $10 = {
  name = "name"
  types = 0x0000000100000f58 "@16@0:8"
  imp = 0x0000000100000dc0 (KCObjc`-[LGHPerson name] at main.m:15)
}
(lldb) p $7.get(3)
(method_t) $11 = {
  name = "setName:"
  types = 0x0000000100000f60 "v24@0:8@16"
  imp = 0x0000000100000de0 (KCObjc`-[LGHPerson setName:] at main.m:15)
}

数组越界,说明类信息里面只存储实例方法不存储类方法

(lldb) p $7.get(4)
Assertion failed: (i < count), function get, file /Users/xiaofeiguan/Desktop/04-isa指向&类初探/03-自己补充/可编译objc源码/runtime/objc-runtime-new.h, line 438.
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.
上一篇 下一篇

猜你喜欢

热点阅读