iOS - 类的加载过程(扩展/关联对象)

606 阅读4分钟

ios-类的加载过程(分类的加载过程)

前言:

上篇文章分析了分类的加载过程,是一个二维数组的方式,加载分类的要求本类必须实例话话,那么和分类对比,类的另外一个模块类扩展是如何实现的,我们都知道,分类的属性是没有实现setter和getter方法的,那么类扩展是否实现,如果给分类添加属性的setter和getter方法如何实现?本章内容就研究这两个问题,类扩展分类的关联对象。

概念:

category: 类别,分类

  • 专门用来给类添加新的方法

  • 不能给类添加成员属性,成员变量也无法取到

  • 分类中用@property定义变量,只会生成setter和getter方法声明,不能生成方法实现和带下划线的成员变量

  • 分类可以通过runtime的api给分类添加属性

extension 类扩展

  • 可以说是特殊的分类,也称匿名分类

  • 可以给类添加成员变量,但他们都是私有变量

  • 可以给类添加方法,但他们都是私有方法

extension类扩展分析

类扩展的本质

写一个类扩展

通过clang -rewrite-objc main.mm -o main.cpp命令生成cpp文件,打开cpp文件,搜索ext_name属性

由上图可看出类的扩展是作为类的一部分一起实现的加入类的方法列表中

类的扩展只有声明没有具体的实现,它是依赖与类进行实现的

关联对象分析

相关方法

  • objc_AssociationPolicy

这是在调用objc_setAssociatedObject时需要用到的参数,用于指定关联参数的引用策略。

  • OBJC_ASSOCIATION_ASSIGN

    将关联引用描述为弱引用

  • OBJC_ASSOCIATION_RETAIN_NONATOMIC

    将关联引用描述为强引用,并且非原子性。

  • OBJC_ASSOCIATION_COPY_NONATOMIC

    将关联引用描述为拷贝引用,并且非原子性。

  • OBJC_ASSOCIATION_RETAIN

    将关联引用描述为强引用,并且为原子性。

  • OBJC_ASSOCIATION_COPY

    将关联引用表述为拷贝引用,并且为原子性。

这一段比较好理解,与Property一样,关联引用也可以设置引用描述。

  • objc_setAssociatedObject

创建和设置一个关联对象的方法。

将一个选定值(value)通过Key-Value的形式挂载在目标对象(object)上,同时指定关联的策略(policy),这样就能生成一个关联对象。

通过将目标对象(object)上指定的Key对应的值(value)设置nil,即可以将已存在的关联对象清除。

  • objc_getAssociatedObject

关联对象值的获取方法。

通过关联Key,在目标对象(object)中,获取到关联对象的值(返回值)。

  • objc_removeAssociatedObjects

移除目标对象(objct)的所有关联对象。

官方在这做了提示:这个方法的主要目的是为了让对象更容易返回到原始状态,调用此方法会将绑定在这个目标对象上的所有关联对象都清除。如果别的开发也为这个object设置了关联对象,或者你在别的模块中也为其设置了不同的关联对象,调用此方法后会被一并删除。通常清除关联对象,请通过objc_setAssociatedObject将关联对象设置为nil的方式来清除关联对象。

底层实现

  • 进入源码,搜索**objc_setAssociatedObject**方法

  • 点击进入**SetAssocHook**

  • 搜索**_base_objc_setAssociatedObject**,进入**_object_set_associative_reference**

  • 探究{代码块中的内容} 【研究核心】

关联核心代码探索

AssociationsManager/AssociationsHashMap

  • 初始化一个AssociationsManager变量manager,它不是一个单例,只是用它来给下面访问哈希表进行加锁

  • 定义AssociationsHashMap类型的哈希map,这个全场唯一的,从哪里可以体现呢?

具体流程

  • 通过manager.get()函数获取到关联对象的哈希表的引用&associations,类型是AssociationsHashMap

  • 判断value存在时:

  • 从哈希表中读取disguised对应的结果refs_resultdisguised就是传入的object参数。

  • 通过结果refs_result中的second判断是不是第一次来。如果第一次来就会调用object->setHasAssociatedObjects();函数

  • setHasAssociatedObjects就是给对象object设置状态。代表这个对象是有关联对象存在的。如果objectnonpointer,那么这个关联状态就存储在isa

  • 然后根据参数key创建关联,如果之前关联过就进行替换关联。

  • 此处就可以知道,是两层哈希表。第一层是以对象作为哈希函数,第二层是以闯入的参数key为哈希函数。

  • value不存在时:传入的参数value为nil。就进行擦除。因为是双层哈希,所以得做两层擦除。

流程图如下:

关联对象get

objc_getAssociatedObject

_object_get_associative_reference

关联对象remove

objc_removeAssociatedObjects

_object_remove_assocations

总结:

HashMap的表结构:

关联对象的底层调用流程如下图所示