IMG_5660.JPG

Q1:为什么要有ro、rw、rwe

  • 沙盒中的数据,ro是readOnly,所以需要改变进行copy,变成rw
  • ro(class_ro_t) -> clean memory是指加载后不会发生更改的内存,它是只读的。可以从内存中移除,需要时再从磁盘加载进来。
  • rw(class_rw_t) -> dirty memory是指在进程运行时会发生更改的内存,它是可读可写的。类结构一经使用就会变成dirty memory,因为运行时会向它写入新的数据。rw相对于iOS是非常昂贵,程序运行就一直存在。
    • rw,从ro复制过来,脏内存、昂贵,存储类的父类、名字
    • rwe,rw的扩展extension,运行时动态添加,存储类的方法、协议、属性,当前类有一个标识,ext,代表有扩展

未命名文件 (6).jpg

Q2:cls->data()来源

  • realizeClassWithoutSwift中的ro为(const class_ro_t *)cls->data(),
  • cls->data()引申调用到bits.data()

    1. class_rw_t *data() const {
    2. return bits.data();
    3. }
  • bits.data()是通过bits & mask 得到地址指针返回

    1. class_rw_t* data() const {
    2. return (class_rw_t *)(bits & FAST_DATA_MASK);
    3. }
  • 得到了地址指针,按class_ro_t结构体类型强转后可以按其内存结构依次赋值

    1. struct class_ro_t {
    2. uint32_t flags;
    3. uint32_t instanceStart;
    4. uint32_t instanceSize;
    5. #ifdef __LP64__
    6. uint32_t reserved;
    7. #endif
    8. ...
    9. }

    Q3: rwe什么时候赋值的

  • extAllocIfNeeded -> rwe,extAllocIfNeeded什么时候调用呢?

    attachCategories

  • rwe取值取决于extAllocIfNeed,分类在什么时候加载取决于attachCategories

  • 全局搜索attachCategories,调用其有两处

    • load_categories_nolock -> attachCategories
    • attachToClass -> attachCategories
    • attachToClass 反向查找methodizeClass, methodizeClass反向查找realizeClassWithoutSwift,所以流程为realizeClassWithoutSwift -> methodizeClass -> attachToClass -> attachCategories -> attachList

      attachList

      算法

  • 0 lists -> 1 list, 一维,list指针指向添加数组的首地址

  • 1 list -> many lists,二维,根据oldCount和addedCount大小开辟空间,将添加的数组元素插入在新开辟空间的头部,oldList作为一个整体添加在新开辟空间尾部
  • many lists -> many lists,二维,根据oldCount和addedCount大小开辟空间,先倒叙排列,将上个步骤的oldList和addedCount依次插入在新开辟空间的尾部,再插入新的added内容

    1. void attachLists(List* const * addedLists, uint32_t addedCount) {
    2. if (addedCount == 0) return;
    3. if (hasArray()) {
    4. // many lists -> many lists
    5. uint32_t oldCount = array()->count;
    6. uint32_t newCount = oldCount + addedCount;
    7. array_t *newArray = (array_t *)malloc(array_t::byteSize(newCount));
    8. newArray->count = newCount;
    9. array()->count = newCount;
    10. for (int i = oldCount - 1; i >= 0; i--)
    11. newArray->lists[i + addedCount] = array()->lists[i];
    12. for (unsigned i = 0; i < addedCount; i++)
    13. newArray->lists[i] = addedLists[i];
    14. free(array());
    15. setArray(newArray);
    16. validate();
    17. }
    18. else if (!list && addedCount == 1) {
    19. // 0 lists -> 1 list
    20. list = addedLists[0];
    21. validate();
    22. }
    23. else {
    24. // 1 list -> many lists
    25. Ptr<List> oldList = list;
    26. uint32_t oldCount = oldList ? 1 : 0;
    27. uint32_t newCount = oldCount + addedCount;
    28. setArray((array_t *)malloc(array_t::byteSize(newCount)));
    29. array()->count = newCount;
    30. if (oldList) array()->lists[addedCount] = oldList;
    31. for (unsigned i = 0; i < addedCount; i++)
    32. array()->lists[i] = addedLists[i];
    33. validate();
    34. }
    35. }

    step2
    IMG_93A4CA17149D-1.jpeg
    step3
    IMG_944543C1ACFE-1.jpeg

    分类、类搭配加载

    主类、分类都实现load方法

  • 流程为_read_images 非懒加载类 -> realizeClassWithoutSwift ->methodizeClass -> attachToClass -> load_categories_nolock -> attachCategories

    • realizeClassWithoutSwift,加载主类
    • load_categories_nolock,加载分类

image.png

分类实现load方法,主类没有

  • 分类有load,主类没有,主类被迫营业,实现非懒加载,通过data数据获取(llvm实现)
  • 流程为_read_images 非懒加载类 -> realizeClassWithoutSwift -> methodizeClass -> attachToClass - 没有走attachCategories

image.png

  • 断点realizeClassWithoutSwift,进行LLDB调试,观察是否有分类方法,发现分类方法并未加载到ro中

    1. (lldb) p ro
    2. (const class_ro_t *) $0 = 0x00000001000044b0
    3. (lldb) p *$0
    4. (const class_ro_t) $1 = {
    5. flags = 0
    6. instanceStart = 8
    7. instanceSize = 40
    8. reserved = 0
    9. = {
    10. ivarLayout = 0x0000000000000000
    11. nonMetaclass = nil
    12. }
    13. name = {
    14. std::__1::atomic<const char *> = "LGPerson" {
    15. Value = 0x0000000100003c3a "LGPerson"
    16. }
    17. }
    18. baseMethodList = 0x00000001000044f8
    19. baseProtocols = nil
    20. ivars = 0x00000001000045f0
    21. weakIvarLayout = 0x0000000000000000
    22. baseProperties = 0x0000000100004698
    23. _swiftMetadataInitializer_NEVER_USE = {}
    24. }
    25. (lldb) p $1.baseMethod()
    26. error: <user expression 2>:1:4: no member named 'baseMethod' in 'class_ro_t'
    27. $1.baseMethod()
    28. ~~ ^
    29. (lldb) p $1.baseMethods()
    30. (method_list_t *) $2 = 0x00000001000044f8
    31. (lldb) p *$2
    32. (method_list_t) $3 = {
    33. entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 24, count = 10)
    34. }
    35. (lldb) p $3.get(0).big
    36. (method_t::big) $4 = {
    37. name = "saySomething"
    38. types = 0x0000000100003de8 "v16@0:8"
    39. imp = 0x0000000100003a00 (KCObjcBuild`-[LGPerson saySomething])
    40. }
    41. Fix-it applied, fixed expression was:
    42. $3.get(0).big()
    43. (lldb) p $3.get(1).big
    44. (method_t::big) $5 = {
    45. name = "sayHello1"
    46. types = 0x0000000100003de8 "v16@0:8"
    47. imp = 0x0000000100003a30 (KCObjcBuild`-[LGPerson sayHello1])
    48. }
    49. Fix-it applied, fixed expression was:
    50. $3.get(1).big()
    51. (lldb) p $3.get(2).big
    52. (method_t::big) $6 = {
    53. name = "name"
    54. types = 0x0000000100003dfc "@16@0:8"
    55. imp = 0x0000000100003a60 (KCObjcBuild`-[LGPerson name])
    56. }
    57. Fix-it applied, fixed expression was:
    58. $3.get(2).big()
    59. (lldb) p $3.get(3).big
    60. (method_t::big) $7 = {
    61. name = "setName:"
    62. types = 0x0000000100003e04 "v24@0:8@16"
    63. imp = 0x0000000100003a90 (KCObjcBuild`-[LGPerson setName:])
    64. }
    65. Fix-it applied, fixed expression was:
    66. $3.get(3).big()
    67. (lldb) p $3.get(4).big
    68. (method_t::big) $8 = {
    69. name = "age"
    70. types = 0x0000000100003e13 "i16@0:8"
    71. imp = 0x0000000100003ac0 (KCObjcBuild`-[LGPerson age])
    72. }
    73. Fix-it applied, fixed expression was:
    74. $3.get(4).big()
    75. (lldb) p $3.get(5).big
    76. (method_t::big) $9 = {
    77. name = "setAge:"
    78. types = 0x0000000100003e1b "v20@0:8i16"
    79. imp = 0x0000000100003ae0 (KCObjcBuild`-[LGPerson setAge:])
    80. }
    81. Fix-it applied, fixed expression was:
    82. $3.get(5).big()
    83. (lldb) p $3.get(6).big
    84. (method_t::big) $10 = {
    85. name = "height"
    86. types = 0x0000000100003e26 "c16@0:8"
    87. imp = 0x0000000100003b00 (KCObjcBuild`-[LGPerson height])
    88. }
    89. Fix-it applied, fixed expression was:
    90. $3.get(6).big()
    91. (lldb) p $3.get(7).big
    92. (method_t::big) $11 = {
    93. name = "setHeight:"
    94. types = 0x0000000100003e2e "v20@0:8c16"
    95. imp = 0x0000000100003b20 (KCObjcBuild`-[LGPerson setHeight:])
    96. }
    97. Fix-it applied, fixed expression was:
    98. $3.get(7).big()
    99. (lldb) p $3.get(8).big
    100. (method_t::big) $12 = {
    101. name = "nickName"
    102. types = 0x0000000100003dfc "@16@0:8"
    103. imp = 0x0000000100003b40 (KCObjcBuild`-[LGPerson nickName])
    104. }
    105. Fix-it applied, fixed expression was:
    106. $3.get(8).big()
    107. (lldb) p $3.get(9).big
    108. (method_t::big) $13 = {
    109. name = "setNickName:"
    110. types = 0x0000000100003e04 "v24@0:8@16"
    111. imp = 0x0000000100003b70 (KCObjcBuild`-[LGPerson setNickName:])
    112. }
    113. Fix-it applied, fixed expression was:
    114. $3.get(9).big()
  • 接着继续流程,在load_categories_nolock中断点,LLDB调试,发现此时出现了分类,其name是LGA,cls是LGPerson

    1. (lldb) p count
    2. (size_t) $14 = 1
    3. (lldb) p cat
    4. (category_t *) $15 = 0x00000001000043d8
    5. (lldb) p *$15
    6. (category_t) $16 = {
    7. name = 0x0000000100003c36 "LGA"
    8. cls = 0x00000001000047f8
    9. instanceMethods = {
    10. ptr = 0x0000000100004310
    11. }
    12. classMethods = {
    13. ptr = 0x0000000100004360
    14. }
    15. protocols = nil
    16. instanceProperties = 0x00000001000043b0
    17. _classProperties = nil
    18. }
    19. (lldb) p cls
    20. (Class) $17 = LGPerson
  • 接着继续流程,load_categories_nolock会调用attachCategories,取出methodList,调用rwe->methods.attachLists,attachlist如上分析,二维指针

    主类实现load方法,分类没有

  • 主类实现load,分类同样会实现load,同样通过data数据获取

  • 流程为_read_images 非懒加载类 -> realizeClassWithoutSwift -> methodizeClass -> attachToClass - 没有走attachCategories

image.png

主类、分类均未实现load方法

  • 推迟到第一次消息发送时才初始化,加载已经实现在data中(llvm帮我们实现好,存在mach-o中)

    多个分类情况

    如果主类没有实现load,只有一个分类实现了load方法,啥也不会走,如果有两个分类实现了,会走prepare_load_methods,然后调起realizeClassWithoutSwift使主类被迫营业,这种情况就跟我们第一种情况一样,会走attachCategories方法了。。另外,如果新建了一个分类没有做任何操作,是不计入总的分类数量的,在cats_count中只计入已经有实现方法的分类(即使没有load方法也可以)。

    补充

  • 分类加载是否需要排序

    • ※LGA数组指针(指针已排序prepareList排序)->LGB数组指针->主类数组指针,二分查找时,进行—操作,最外层也是array_t
    • ※如果主类、分类都实现了load方法,则通过传入的cls加载,LGA、LGB、主类分开加载,是二维指针,不需要排序
    • ※而其它情况都被实现在ro->data数据端中,二分查找取出时需要排序处理,找到最前面的,是一维指针
    • 分类加载顺序取决于编译顺序,可以在Build Phases -> Compile Sources中修改
  • methodList 数据结构

    • 本质是array_t数据结构entsize_list_tt,存储的是method指针,而不是method数据结构
    • entsize_list_tt结构体中,取值是通过get方法,计算地址拿到指针
      1. struct entsize_list_tt {
      2. ...
      3. Element& getOrEnd(uint32_t i) const {
      4. ASSERT(i <= count);
      5. return *PointerModifier::modify(*this, (Element *)((uint8_t *)this + sizeof(*this) + i*entsize()));
      6. }
      7. ...
      8. }
      image.png
  • 主类没有load、分类实现了,数据加载问题

    • 超过一个分类实现了load,主类被迫营业
    • 音视频设备模块框架图.jpg
  • class_ro_t:指针地址,获取数据格式