前言:父类是子类的第一个成员变量,子类就拥有了父类的所有成员变量,叫结构体的伪继承
IMG_5444.JPG

Class底层

Class是objc_class结构体指针,是objc_class的别名。

  1. typedef struct objc_class *Class;

objc_class伪继承于objc_object,因此包含了isa,此外还有superclass、cache、bits几个成员变量,分析结构体内存,只需计算其成员变量大小。

  1. struct objc_class : objc_object {
  2. objc_class(const objc_class&) = delete;
  3. objc_class(objc_class&&) = delete;
  4. void operator=(const objc_class&) = delete;
  5. void operator=(objc_class&&) = delete;
  6. // Class ISA;
  7. Class superclass;
  8. cache_t cache; // formerly cache pointer and vtable
  9. class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags

isa

元类分析

首先,我们建立一个person类,断点调试,通过lldb得到p的指针地址0x108cc2650

  1. //mask 0x00007ffffffffff8
  2. LGPerson *p = [LGPerson alloc];
  3. (lldb) x p
  4. 0x108cc2650: 65 83 00 00 01 80 1d 01 00 00 00 00 00 00 00 00 e...............
  5. 0x108cc2660: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................

然后lldb命令x/4gx得到它的类内存结构,第一个为isa的地址0x011d800100008365。通过OC本质探索一文,我们可以通过isa & mask得到类地址0x0000000100008360po得到了person

  1. (lldb) x/4gx 0x108cc2650
  2. 0x108cc2650: 0x011d800100008365 0x0000000000000000
  3. 0x108cc2660: 0x0000000000000000 0x0000000000000000
  4. (lldb) p/x 0x011d800100008365 & 0x00007ffffffffff8
  5. (long) $13 = 0x0000000100008360
  6. (lldb) po 0x0000000100008360
  7. LGPerson

此时,我们再通过x/4gx 类地址0x0000000100008360,又得到一个新的类内存结构,poisa & mask竟然也得到了Person类,其地址是0x0000000100008338,这个类地址和上面的0x0000000100008360哪一个是真正的person类地址呢?
1623976300716.jpg
通过打印类地址,确定person类,其地址为0x100008360image.png
那另外一个地址是什么?我们可以通过MachOView分析Mach-o文件,首先确定了person类地址为0x100008360
image.png
在符号表中,我们找到了内存地址为0x0000000100008338的类,它是_objc_metaClass_LGPerson类型,是一个元类,由person类的isa指向,元类是由系统编译时自动生成的image.png

根元类分析

继续上面的逻辑,p/x 元类isa & maskpo 元类isa & mask得到的了NSObject类,所以person元类的isa指向了NSObject,这里NSObject的isa与superClass地址相同,都是0x00007fff8897eca0

  1. (lldb) x/4gx 0x0000000100008338
  2. 0x100008338: 0x00007fff8897eca0 0x00007fff8897eca0
  3. 0x100008348: 0x0000000108cc2670 0x0002e03500000003
  4. (lldb) p/x 0x00007fff8897eca0 & 0x00007ffffffffff8
  5. (long) $28 = 0x00007fff8897eca0
  6. (lldb) po 0x00007fff8897eca0
  7. NSObject

重复操作上面逻辑,观察NSObject类的isa指向了自己,这时执行p/x NSObject.classx/4gx,发现其isa地址同样为0x00007fff8897eca0,所以这里NSObject为根元类

  1. (lldb) x/4gx 0x00007fff8897eca0
  2. 0x7fff8897eca0: 0x00007fff8897eca0 0x00007fff8897ecc8
  3. 0x7fff8897ecb0: 0x0000000108b06f60 0x0005e03100000007
  4. (lldb) p/x 0x00007fff8897eca0 & 0x00007ffffffffff8
  5. (long) $30 = 0x00007fff8897eca0
  6. (lldb) po 0x00007fff8897eca0
  7. NSObject
  8. (lldb) p/x NSObject.class
  9. (Class) $32 = 0x00007fff8897ecc8 NSObject
  10. (lldb) x/4gx 0x00007fff8897ecc8
  11. 0x7fff8897ecc8: 0x00007fff8897eca0 0x0000000000000000
  12. 0x7fff8897ecd8: 0x0000000108cbc610 0x0001801000000003

isa流程

未命名文件.jpg

superClass

通过以下代码打印,得到superClass的对应关系

  1. void lgTestNSObject(void){
  2. // NSObject实例对象
  3. NSObject *object1 = [NSObject alloc];
  4. // NSObject类
  5. Class class = object_getClass(object1);
  6. // NSObject元类
  7. Class metaClass = object_getClass(class);
  8. // NSObject根元类
  9. Class rootMetaClass = object_getClass(metaClass);
  10. // NSObject根根元类
  11. Class rootRootMetaClass = object_getClass(rootMetaClass);
  12. NSLog(@"\n%p 实例对象\n%p 类\n%p 元类\n%p 根元类\n%p 根根元类",object1,class,metaClass,rootMetaClass,rootRootMetaClass);
  13. // LGPerson元类
  14. Class pMetaClass = object_getClass(LGPerson.class);
  15. Class psuperClass = class_getSuperclass(pMetaClass);
  16. NSLog(@"%@ - %p",psuperClass,psuperClass);
  17. // LGTeacher -> LGPerson -> NSObject
  18. // 元类也有一条继承链
  19. Class tMetaClass = object_getClass(LGTeacher.class);
  20. Class tsuperClass = class_getSuperclass(tMetaClass);
  21. NSLog(@"%@ - %p",tsuperClass,tsuperClass);
  22. // NSObject 根类特殊情况
  23. Class nsuperClass = class_getSuperclass(NSObject.class);
  24. NSLog(@"%@ - %p",nsuperClass,nsuperClass);
  25. // 根元类 -> NSObject
  26. Class rnsuperClass = class_getSuperclass(metaClass);
  27. NSLog(@"%@ - %p",rnsuperClass,rnsuperClass);
  28. }
  29. 0x10893d4c0 实例对象
  30. 0x7fff8897ecc8
  31. 0x7fff8897eca0 元类
  32. 0x7fff8897eca0 根元类
  33. 0x7fff8897eca0 根根元类
  34. NSObject - 0x7fff8897eca0
  35. LGPerson - 0x100008338
  36. (null) - 0x0
  37. NSObject - 0x7fff8897ecc8

superClass流程

未命名文件-2.jpg

cache

cache_t同样是结构体,其大小与变量有关

  • _bucketsAndMaybeMaskuintptr_t类型,就是unsignedlong ,大小为8字节
  • union为联合体,取内存较大成员变量
    • _maybeMaskmask_t(uint32_t)类型,占4字节,_flagsuint16_t,占2字节,_occupied为uint16_t,占2字节
    • _originalPreoptCache为结构体指针,占8个字节
  • 因此cache大小为16字节

    1. struct cache_t {
    2. private:
    3. explicit_atomic<uintptr_t> _bucketsAndMaybeMask;
    4. union {
    5. struct {
    6. explicit_atomic<mask_t> _maybeMask;
    7. #if __LP64__
    8. uint16_t _flags;
    9. #endif
    10. uint16_t _occupied;
    11. };
    12. explicit_atomic<preopt_cache_t *> _originalPreoptCache;
    13. };

    bits

    bits在底层是class_data_bits_t结构体,而class_data_bits_t关键就是返回class_rw_t指针 ``` struct class_data_bits_t { friend objc_class;

    // Values are the FAST_ flags above. uintptr_t bits; private: …

public:

  1. class_rw_t* data() const {
  2. return (class_rw_t *)(bits & FAST_DATA_MASK);
  3. }
  4. ...

}

  1. `class_rw_t`同样是一个结构体,其包含了`ro、method_array_t、property_array_t、protocol_array_t`方法调用<br />![1624002856890.jpg](https://cdn.nlark.com/yuque/0/2021/jpeg/21860440/1624002968995-b326f2fe-7852-4801-93a1-7e672094035d.jpeg#clientId=u6e1da652-1a75-4&from=drop&id=u96486672&margin=%5Bobject%20Object%5D&name=1624002856890.jpg&originHeight=1010&originWidth=1742&originalType=binary&ratio=1&size=210350&status=done&style=none&taskId=u57a82d04-89e8-45e2-8247-24a875ee243)<br />我们可以通过lldb调试验证下bits中包含的属性,在类结构体中(方法存储在方法区,不占用结构体内存)
  2. - 第一成员变量`isa`,占8字节
  3. - 第二成员变量`superClass`,占8字节
  4. - 第三成员变量`cache`,占16字节
  5. 因此,第四成员变量`bits`地址,可以通过类地址 + 32得到

(lldb) p/x LGPerson.class (Class) $3 = 0x0000000100008380 LGPerson (lldb) p (class_data_bits_t)(0x0000000100008380 + 32) (class_data_bits_t) $4 = (bits = 4441863924)

  1. 得到class_data_bits_t后,可以依次取出其中的propertymethodivars

(lldb) p $4.data() (class_rw_t ) $5 = 0x0000000108c176f0 (lldb) p ($5).properties() (const property_array_t) $6 = { list_array_tt = { = { list = { ptr = 0x0000000100008260 } arrayAndFlag = 4295000672 } } } (lldb) p $6.list.ptr (property_list_t const) $7 = 0x0000000100008260 (lldb) p $7 (property_list_t) $8 = { entsize_list_tt = (entsizeAndFlags = 16, count = 2) } (lldb) p $8.get(0) (property_t) $9 = (name = “name”, attributes = “T@\”NSString\”,C,N,V_name”) (lldb) p $8.get(1) (property_t) $10 = (name = “hobby”, attributes = “T@\”NSString\”,C,N,V_hobby”)

  1. ```
  2. (lldb) p/x LGPerson.class
  3. (Class) $0 = 0x0000000100008380 LGPerson
  4. (lldb) p *(class_data_bits_t*)(0x0000000100008380 + 32)
  5. (class_data_bits_t) $1 = (bits = 4443234692)
  6. (lldb) p $1.data()
  7. (class_rw_t *) $2 = 0x0000000108d66180
  8. (lldb) p *$2
  9. (class_rw_t) $3 = {
  10. flags = 2148007936
  11. witness = 1
  12. ro_or_rw_ext = {
  13. std::__1::atomic<unsigned long> = {
  14. Value = 4295000344
  15. }
  16. }
  17. firstSubclass = nil
  18. nextSiblingClass = NSUUID
  19. }
  20. (lldb) p $3.methods()
  21. (const method_array_t) $4 = {
  22. list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = {
  23. = {
  24. list = {
  25. ptr = 0x0000000100008160
  26. }
  27. arrayAndFlag = 4295000416
  28. }
  29. }
  30. }
  31. (lldb) p $4.list.ptr
  32. (method_list_t *const) $5 = 0x0000000100008160
  33. (lldb) p *$5
  34. (method_list_t) $6 = {
  35. entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 27, count = 6)
  36. }
  37. (lldb) p $6.get(0).big
  38. (method_t::big) $7 = {
  39. name = "hobby"
  40. types = 0x0000000100003f53 "@16@0:8"
  41. imp = 0x0000000100003dc0 (KCObjcBuild`-[LGPerson hobby])
  42. }
  43. Fix-it applied, fixed expression was:
  44. $6.get(0).big()
  45. (lldb) p $6.get(1).big
  46. (method_t::big) $8 = {
  47. name = "sayNB"
  48. types = 0x0000000100003f5b "v16@0:8"
  49. imp = 0x0000000100003d50 (KCObjcBuild`-[LGPerson sayNB])
  50. }
  51. Fix-it applied, fixed expression was:
  52. $6.get(1).big()
  53. (lldb) p $6.get(2).big
  54. (method_t::big) $9 = {
  55. name = "setHobby:"
  56. types = 0x0000000100003f6f "v24@0:8@16"
  57. imp = 0x0000000100003df0 (KCObjcBuild`-[LGPerson setHobby:])
  58. }
  59. Fix-it applied, fixed expression was:
  60. $6.get(2).big()
  61. (lldb) p $6.get(3).big
  62. (method_t::big) $10 = {
  63. name = "init"
  64. types = 0x0000000100003f53 "@16@0:8"
  65. imp = 0x0000000100003cf0 (KCObjcBuild`-[LGPerson init])
  66. }
  67. Fix-it applied, fixed expression was:
  68. $6.get(3).big()
  69. (lldb) p $6.get(4).big
  70. (method_t::big) $11 = {
  71. name = "name"
  72. types = 0x0000000100003f53 "@16@0:8"
  73. imp = 0x0000000100003d60 (KCObjcBuild`-[LGPerson name])
  74. }
  75. Fix-it applied, fixed expression was:
  76. $6.get(4).big()
  77. (lldb) p $6.get(5).big
  78. (method_t::big) $12 = {
  79. name = "setName:"
  80. types = 0x0000000100003f6f "v24@0:8@16"
  81. imp = 0x0000000100003d90 (KCObjcBuild`-[LGPerson setName:])
  82. }
  83. Fix-it applied, fixed expression was:
  84. $6.get(5).big()
  1. (lldb) p/x LGPerson.class
  2. (Class) $0 = 0x0000000100008380 LGPerson
  3. (lldb) p *(class_data_bits_t *)(0x0000000100008380 + 32)
  4. (class_data_bits_t) $1 = (bits = 4441981524)
  5. (lldb) p $1.data()
  6. (class_rw_t *) $2 = 0x0000000108c34250
  7. (lldb) p (*$2).ro()
  8. (const class_ro_t *) $3 = 0x0000000100008118
  9. (lldb) p (*$3).ivars
  10. (const ivar_list_t *const) $4 = 0x00000001000081f8
  11. (lldb) p *$4
  12. (const ivar_list_t) $5 = {
  13. entsize_list_tt<ivar_t, ivar_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 32, count = 3)
  14. }
  15. (lldb) p $5.get(0)
  16. (ivar_t) $6 = {
  17. offset = 0x0000000100008318
  18. name = 0x0000000100003f19 "subject"
  19. type = 0x0000000100003f63 "@\"NSString\""
  20. alignment_raw = 3
  21. size = 8
  22. }
  23. (lldb) p $5.get(1)
  24. (ivar_t) $7 = {
  25. offset = 0x0000000100008320
  26. name = 0x0000000100003f21 "_name"
  27. type = 0x0000000100003f63 "@\"NSString\""
  28. alignment_raw = 3
  29. size = 8
  30. }
  31. (lldb) p $5.get(2)
  32. (ivar_t) $8 = {
  33. offset = 0x0000000100008328
  34. name = 0x0000000100003f27 "_hobby"
  35. type = 0x0000000100003f63 "@\"NSString\""
  36. alignment_raw = 3
  37. size = 8
  38. }

总结

  • class内存结构

未命名文件-3.jpg