当前位置:   article > 正文

iOS 底层探索篇 ——类的加载原理补充,类拓展和关联对象_分类 多个 methodlist

分类 多个 methodlist

1.分类加载是否需要排序

由上一篇文章知道,在方法列表中,2个分类的方法的数组指针是在主类之前的,并且数组指针里面是经过排序的。
首先添加2个分类,并且类和2个分类都有一个方法叫做testfunc1
运行,看到getMethodNoSuper_nolock里面。看到这里是遍历查找
在这里插入图片描述
输出看到beginLists分类LS
在这里插入图片描述
array_t里面存的是3个2维指针,这个指针没有必要进行排序,他们各自有各自的排序。取methodList的时候进行了遍历查找,也说明了分类加载不需要进行排序。
那么2分查找的时候需要进行probe--呢?因为当类和分类都在data数据段被读取的时候,方法整合到一块了,所以需要probe–去找到最前面的那个方法。
在这里插入图片描述

2.methodList的数据结构

methodList的数据结构是method_list_t,继承自entsize_list_tt
在这里插入图片描述
从上篇文章中可以知道,methodList是以指针方式存储的。
在这里插入图片描述
而当需要获取其中的元素时候,需要调用get方法来获取,这也说明了method_list_t里面存的也是指针而不是值,否则的话是可以以数组的方式取出来的。而指针则指向了method_t
在这里插入图片描述
在看到get方法,可以看出get方法是通过地址平移来获取到指针地址的。
在这里插入图片描述

3.主类没有实现load方法,分类实现load方法时的数据加载

当主类没有实现load方法,那么分类时候load方法时候情况会是什么样的呢?
首先创建3个分类,其中2个分类里面有方法和属性,剩下一个没有
在有方法和属性的分类中,选择一个实现load方法
运行后发现主类非懒加载,但是分类都没有加载
在这里插入图片描述
两个分类都实现load方法,运行后可以发现分类加载进去了
在这里插入图片描述
这里可以得出,当2个或以上的分类实现了load方法,那么分类就会进行非懒加载。
在看到attachCategories的时候,cats_count为2,那么是哪个分类没有加载进去呢?

在这里插入图片描述
输出来看一下哪个分类没有加载。这里看到LSLSS加载了,LSSS也就是没有方法和属性的分类没有加载,说明了系统做了优化,当分类什么都没有的时候,就不会被加载浪费性能。
在这里插入图片描述
在这里插入图片描述

在看到load_images里面,发现走的是prepare_load_methods而不是正常的loadAllCategories方法。prepare_load_methods也就是迫使主类实现的地方,
在这里插入图片描述
看到里面的prepare_load_methods的实现,发现确实让主类进行了非懒加载,也就是让主类被迫营业了。
在这里插入图片描述

4.class_ro_t

在llvm看一下class_ro_t。
在这里插入图片描述
看到这里有个read,查找一下read。看到read里面进行了地址的处理,然后获得了extractor,然后进行了各个属性进行了赋值
在这里插入图片描述

在这里插入图片描述

5.类扩展

分类:categoty

  • 专门用来给类添加新方法。
  • 不能给类添加成员属性,添加了成员变量也无法取到,但是可以通过runtime给分类添加属性,即属性关联,重写setter、getter方法。
  • 分类中用@property定义变量,只会生成变量的getter,setter方法的声明,不能生成方法实现和带下划线的成员变量。

扩展:extension

  • 特殊的分类,也叫做匿名分类。
  • 可以给类添加成员属性,但是是私有变量。
  • 可以给类添加方法,也是私有方法。

这就是扩展,其必须定义在@interface 和 @implementation 之间
在这里插入图片描述
clang 一下 main.m 后打开。发现拓展的方法以及属性已经存在了
在这里插入图片描述
在这里插入图片描述
那么类扩展是否会和分类那样有一个自己的结构体呢?找了一圈发现没有。分类会影响到类的加载,那么拓展会吗?
LGPerson添加一个类扩展并添加方法,然后在LGPerson中添加方法的实现。LGPerson实现load方法,在realizeClassWithoutSwift打下断点然后运行。
运行后在realizeClassWithoutSwift输出ro的方法列表然后观察,发现类扩展已经被加载进去了。也就是说,在类加载的时候,类扩展作为类的一部分已经加载进去了。
在这里插入图片描述

在这里插入图片描述

6.关联对象

关联对象的底层原理的实现,主要分为两部分:

  • 通过objc_setAssociatedObject设值流程
  • 通过objc_getAssociatedObject取值流程

在分类LGA中重写属性cate_nametcate_agesetget方法,通过runtime的属性关联方法也就是objc_setAssociatedObjectobjc_getAssociatedObject实现。

关联对象设值流程

objc_setAssociatedObject的具体实现。这种设计模式属于是接口模式,这种模式下对外的接口不变,也就是外部调用永远是调用objc_setAssociatedObject,内部的逻辑变化不影响外部的调用
在这里插入图片描述

然后来到_object_set_associative_reference里面。看到这里有四个参数。

  • objc:要关联的对象,即给谁添加关联属性
  • key:标识符,方便下次查找
  • value:要存的值
  • policy: 关联策略

往下看,看到了对object 进行了处理。
在这里插入图片描述
点进去查看disguise方法,对ptr进行了处理,也就是参数的处理,所以这里就是对object包装了一下,包装成统一的数据结构
在这里插入图片描述
在往下是ObjcAssociation association{policy, value}
在这里插入图片描述

这里的const void *也就是属性的名字,比如说cate_name,cate_age,这里是键值匹配。而在AssociationsHashMap中,不同对象也对应不同的AssociationsMap,所以就是双层HashMap结构
在这里插入图片描述
运行一下程序,来到_object_set_associative_reference里面。
在这里插入图片描述
输出一下确实是我们设的值。
在这里插入图片描述
其中,AssociationsManager manager 是一个构造函数。模仿着写一个以便更好的理解。

在这里插入图片描述
在这里插入图片描述
运行后发现输出了 KC 来了 和大师班 NB 。
在这里插入图片描述
所以AssociationsManager manager相当于调用了 Ass ociationsManager()~AssociationsManager()
在这里插入图片描述
在看到AssociationsHashMap其实是获取_mapStorage,而_mapStorage是静态变量,所以AssociationsHashMap这张表是单例,是唯一的
在这里插入图片描述
回到_object_set_associative_reference往下走,输出associations,发现里面还没有值。
在这里插入图片描述
再输出refs_result,看到secondtrue,那么就会把 isFirstAssociation 设为 true,看到注释也写着 it’s the first association we make,也就是当第一次关联的时候会把 isFirstAssociation设为true

在这里插入图片描述

refs_result是从associations.try_emplace(disguised, ObjectAssociationMap{})来的,看一下try_emplace方法。看到这里会创建新的桶子,这里的key是LGPerson 的地址
在这里插入图片描述
首先会进入LookupBucketFor去查找是有已经有了桶子,看到两个实现,因为在try_emplace中是BucketT没有 const,所以走的是下面的实现。下面的实现会调用上面的实现。
在这里插入图片描述
看上面的实现这里是得到了哈希的下标
在这里插入图片描述
往下就开始死循环找bucket,没找到就在哈希。
在这里插入图片描述
当第一次进来的时候,LookupBucketFor(Key, TheBucket)是找不到的,所以走到了下面。这里就插入一个空的桶子进去,并且还进行了3/4扩容2倍的操作。

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
然后就得到了 TheBucket
在这里插入图片描述
_object_set_associative_reference往下走,又进入了一次try_emplace
在这里插入图片描述
进去后发现LookupBucketFor(Key, TheBucket)还是没有进去,这是因为
这时候bucket里面还没有东西插进去,如果是空的桶子,LookupBucketFor也会返回false
在这里插入图片描述
这里的又会走到InsertIntoBucket,然后在InsertIntoBucket里面将值插入到first里面
在这里插入图片描述

_object_set_associative_reference中输出result,发现first中有了值。
在这里插入图片描述
而first里面的ptr则是我们存进去的bucket
在这里插入图片描述
而当传一个的值的话,那么就会把这个桶子清除
在这里插入图片描述

关联对象: 设值流程总结

1: 创建一个 AssociationsManager 管理类
2: 获取唯一的全局静态哈希Map
3: 判断是否插入的关联值是否存在:
3.1: 存在走第4步
3.2: 不存在就走 : 关联对象插入空流程
4: 创建一个空的 ObjectAssociationMap 去取查询的键值对
5: 如果发现没有这个 key 就插入一个 空的 BucketT进去 返回
6: 标记对象存在关联对象
7: 用当前 修饰策略 和 值 组成了一个 ObjcAssociation 替换原来 BucketT 中的空
8: 标记一下 ObjectAssociationMap 的第一次为 false

关联对象插入空流程总结

1: 根据 DisguisedPtr 找到 AssociationsHashMap 中的 iterator 迭代查询器
2: 清理迭代器
3: 其实如果插入空置 相当于清除

关联对象流程图。
请添加图片描述

图片来自链接: Style_月月.

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/正经夜光杯/article/detail/1022113
推荐阅读
相关标签
  

闽ICP备14008679号