类扩展与关联对象

1,088 阅读3分钟
  • 准备工作

    1. 添加类扩展,并添加属性、方法
    2. 在分类中添加属性,并实现set、get方法
  • 类扩展

    • 类别和类扩展的区别
      • 类别
        1. 专门给类添加新的方法
        2. 不能给类添加成员属性,添加了成员属性也是无法取到的
        3. 可以通过runtime给分类添加属性(通过关联对象)
        4. 分类中@property定义变量只会生成gettersetter方法的声明,不能生成方法的实现和带下划线的成员变量
      • 类扩展
        1. 可以说是特殊的分类,也叫做匿名分类
        2. 可以给类添加成员属性和方法,但是成员属性和方法都是私有的
    • 类扩展底层探索
      添加类扩展后clong对应文件编译查看编译后的文件clang -rewrite-objc main.m -o main.cpp发现编译时期就已经类扩展中的成员属性和方法添加到类中了,也可以运行源码验证

      总结

      1. 类的扩展在编译时期会作为类的一部分,和类一起编译进来
      2. 类的扩展只是声明,依赖于当前的主类,没有.m文件,可以理解为一个·h文件
  • 关联对象

    1. 分类中特性2、4验证
      注释掉分类中属性点额settergetter方法给分类中的属性赋值然后运行 发现赋值编译是可以通过的,但是运行之后崩溃提示找不到setter方法,初步验证了分类中属性的gettersetter方法的声明是有的但是没有实现,
    2. 分类特性3验证
      实现settergetter方法分别通过objc_setAssociatedObject设置和objc_getAssociatedObject方法取值如下:在运行发现此时既能赋值也能取到值
    3. objc_setAssociatedObjectobjc_getAssociatedObject方法源码分析
      • objc_setAssociatedObject
        1. 搜索源码查看入参含义
        2. policy属性策略objc_AssociationPolicy
        3. _object_set_associative_reference
          通过源码发现objc_setAssociatedObject底层调用的是_object_set_associative_reference方法实现功能再看_object_set_associative_reference的源码实现 通过源码可以总结为以下步骤
          1. 创建AssociationsManager变量
          2. 获取静态哈希map,AssociationsHashMap
          3. 判断value是否存在,存在的话通过try_emplace方法,并创建一个空的 ObjectAssociationMap去取查询的键值对:不存在就走 : 关联对象-插入空流程
          4. 如果发现没有这个key就插入一个空的BucketT进去并返回true这一步主要是在try_emplace方法中实现 LookupBucketFor源码实现 如何辨别try_emplace方法中调用的是哪个方法,通过入参属性修饰符来判断 内部调用的LookupBucketFor方法源码实现
          5. 通过setHasAssociatedObjects方法标记对象存在关联对象即置isa指针的has_assoc属性为true
          6. 用当前policy和value组成了一个ObjcAssociation替换原来BucketT中的空
          7. 标记一下 ObjectAssociationMap的第一次为 false
        4. 关联对象涉及的map结构
      • objc_getAssociatedObject
        1. 搜索源码查看入参含义
        2. _object_get_associative_reference
          通过源码发现底层调用的是_object_get_associative_reference方法来实现功能 再看_object_get_associative_reference源码实现
    • 关联对象流程图