文章目录

  • 前言
  • 一般使用
    • void objc_setAssociatedObject
    • objc_getAssociatedObject
  • 关联对象的实现原理
    • 核心对象
    • 核心对象之间的关系
    • runtime源码
      • objc_setAssociatedObject
      • _object_get_associative_reference
      • _object_remove_assocations

前言

关联对象的使用一般用于给Category添加成员变量

一般使用

我们知道 分类添加属性只能生成setter getter方法的声明
不能自动生成实现 所以我们需要自己实现两个方法

void objc_setAssociatedObject

全称 :

void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)

参数 :

  • id object : 给哪个对象添加属性 这里要给自己添加属性 用self
  • const void *key : 根据key获取关联对象的属性的值 只要是一个指针就好了
  • id value : 关联的值 也就是setter方法传入的值给属性去保存
  • objc_AssociationPolicy policy : 保存策略
typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {OBJC_ASSOCIATION_ASSIGN = 0,           /**< Specifies a weak reference to the associated object. */OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, /**< Specifies a strong reference to the associated object. *   The association is not made atomically. */OBJC_ASSOCIATION_COPY_NONATOMIC = 3,   /**< Specifies that the associated object is copied. *   The association is not made atomically. */OBJC_ASSOCIATION_RETAIN = 01401,       /**< Specifies a strong reference to the associated object.*   The association is made atomically. */OBJC_ASSOCIATION_COPY = 01403          /**< Specifies that the associated object is copied.*   The association is made atomically. */
};

对应内存管理方案找就好了 就跟平常属性添加关键字一样
关联对象需要什么样的内存管理方法 就使用哪个方案

用于setter方法

@property (nonatomic, assign) int weight;

在分类中有这样一个属性
setter方法 :

- (void)setWeight:(int)weight {objc_setAssociatedObject(self, @"weight", [NSNumber numberWithInt:weight], OBJC_ASSOCIATION_ASSIGN);
}

注意 int类型的变量需要转成NSNumber类型使用
不然会报错

[NSNumber numberWithInt:weight]

别的类型一般不需要转

objc_getAssociatedObject

全称 :

id objc_getAssociatedObject(id object, const void *key)

用于getter方法

- (int)weight {return [objc_getAssociatedObject(self, @"weight") intValue];
}

这里需要取出来是一个NSNumber类型 所以后面加了intValue
一般不需要加
setter方法中weight关联了一个NSNumber对象才需要加

关联对象的实现原理

核心对象

  • AssociationsManager
  • AssociationsHashMap
  • ObjectAssociationMap
  • ObjcAssociation

Map可以想象成字典 都是一对一的关系

核心对象之间的关系

class AssociationsManager {// associative references: object pointer -> PtrPtrHashMap.// AssociationsManager中只有一个变量AssociationsHashMapstatic AssociationsHashMap *_map;
public:// 构造函数中加锁AssociationsManager()   { AssociationsManagerLock.lock(); }// 析构函数中释放锁~AssociationsManager()  { AssociationsManagerLock.unlock(); }// 构造函数、析构函数中加锁、释放锁的操作,保证了AssociationsManager是线程安全的AssociationsHashMap &associations() {// AssociationsHashMap 的实现可以理解成单例对象if (_map == NULL)_map = new AssociationsHashMap();return *_map;}
};

AssociationsManager中有一个AssociationsHashMap对象 相当于有一个字典
既然跟字典很像 那就看看什么作为key 什么作为value呢

    class AssociationsHashMap : public unordered_map<disguised_ptr_t, ObjectAssociationMap *, DisguisedPointerHash, DisguisedPointerEqual, AssociationsHashMapAllocator> {public:void *operator new(size_t n) { return ::malloc(n); }void operator delete(void *ptr) { ::free(ptr); }};

看不懂是吧 我也看不懂
大概知道AssociationsHashMap是继承自unordered_map
看看unordered_map

源码太长了
只截取一些重点的说一下

template <class _Key, class _Tp, class _Hash = hash<_Key>, class _Pred = equal_to<_Key>,class _Alloc = allocator<pair<const _Key, _Tp> > >typedef _Key                                   key_type;typedef _Tp                                    mapped_type;typedef pair<const key_type, mapped_type>

大概意思就是以key_typemapped_type作为keyvalue
如果传值的话 是以前面两个参数作为keyvalue

那现在就明白 在AssociationsHashMap
disguised_ptr_t是key
ObjectAssociationMap *是value

所以呢AssociationsHashMap中又存放了一个ObjectAssociationMap对象

再看看ObjectAssociationMap

    class ObjectAssociationMap : public std::map<void *, ObjcAssociation, ObjectPointerLess, ObjectAssociationMapAllocator> {public:void *operator new(size_t n) { return ::malloc(n); }void operator delete(void *ptr) { ::free(ptr); }};

根据上面推测 这次就可以知道
ObjectAssociationMapvoid *作为key ObjcAssociation作为value

所以 ObjcAssociationMap中存放的是 ObjcAssociation对象(关联对象类)

    class ObjcAssociation {uintptr_t _policy;// 值id _value;public:// 构造函数ObjcAssociation(uintptr_t policy, id value) : _policy(policy), _value(value) {}// 默认构造函数,参数分别为0和nilObjcAssociation() : _policy(0), _value(nil) {}uintptr_t policy() const { return _policy; }id value() const { return _value; }bool hasValue() { return _value != nil; }};

总结一下

  • AssociationsManager里面存放了一个AssociationsHashMap对象
  • AssociationsHashMap里面存放了ObjectAssociationMap对象
  • ObjectAssociationMap里面存放了ObjcAssociation对象
  • ObjcAssociation里面存放的就是policy跟value

runtime源码

objc_setAssociatedObject

void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) {_object_set_associative_reference(object, (void *)key, value, policy);
}

_object_set_associative_reference

// 该方法完成了设置关联对象的操作
void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {// retain the new value (if any) outside the lock.// 初始化空的ObjcAssociation(关联对象)ObjcAssociation old_association(0, nil);id new_value = value ? acquireValue(value, policy) : nil;{// 初始化一个managerAssociationsManager manager;AssociationsHashMap &associations(manager.associations());// 获取对象的DISGUISE值,作为AssociationsHashMap的keydisguised_ptr_t disguised_object = DISGUISE(object);if (new_value) {// value有值,不为nil// break any existing association.// AssociationsHashMap::iterator 类型的迭代器AssociationsHashMap::iterator i = associations.find(disguised_object);if (i != associations.end()) {// secondary table exists// 获取到ObjectAssociationMap(key是外部传来的key,value是关联对象类ObjcAssociation)ObjectAssociationMap *refs = i->second;// ObjectAssociationMap::iterator 类型的迭代器ObjectAssociationMap::iterator j = refs->find(key);if (j != refs->end()) {// 原来该key对应的有关联对象// 将原关联对象的值存起来,并且赋新值old_association = j->second;j->second = ObjcAssociation(policy, new_value);} else {// 无该key对应的关联对象,直接赋值即可// ObjcAssociation(policy, new_value)提供了这样的构造函数(*refs)[key] = ObjcAssociation(policy, new_value);}} else {// create the new association (first time).// 执行到这里,说明该对象是第一次添加关联对象// 初始化ObjectAssociationMapObjectAssociationMap *refs = new ObjectAssociationMap;associations[disguised_object] = refs;// 赋值(*refs)[key] = ObjcAssociation(policy, new_value);// 设置该对象的有关联对象,调用的是setHasAssociatedObjects()方法object->setHasAssociatedObjects();}} else {// setting the association to nil breaks the association.// value无值,也就是释放一个key对应的关联对象AssociationsHashMap::iterator i = associations.find(disguised_object);if (i !=  associations.end()) {ObjectAssociationMap *refs = i->second;ObjectAssociationMap::iterator j = refs->find(key);if (j != refs->end()) {old_association = j->second;// 调用erase()方法删除对应的关联对象refs->erase(j);}}}}// release the old value (outside of the lock).// 释放旧的关联对象if (old_association.hasValue()) ReleaseValue()(old_association);
}

上面我们知道了四个核心对象之间的关系
那现在需要知道(id object, const void *key, id value, objc_AssociationPolicy policy)这四个参数 都存放在哪里

id object作为了AssociationsHashMap的key

const void *key作为了AssociationMap的key

id value作为了ObjectAssociation里面的_value

objc_AssociationPolicy policy作为了ObjectAssociation里面的_policy

runtime自己维护了一个全局的AssociationsManager 里面存放了HashMap对象
HashMap通过传入的object找到了对应的AssociationMap 再通过传入的id value找到了对应的关联对象

具体的源码逻辑在上面的注释里面已经写的很清楚了

_object_get_associative_reference

id _object_get_associative_reference(id object, void *key) {id value = nil;uintptr_t policy = OBJC_ASSOCIATION_ASSIGN;{AssociationsManager manager;// 获取到manager中的AssociationsHashMapAssociationsHashMap &associations(manager.associations());// 获取对象的DISGUISE值disguised_ptr_t disguised_object = DISGUISE(object);AssociationsHashMap::iterator i = associations.find(disguised_object);if (i != associations.end()) {// 获取ObjectAssociationMapObjectAssociationMap *refs = i->second;ObjectAssociationMap::iterator j = refs->find(key);if (j != refs->end()) {// 获取到关联对象ObjcAssociationObjcAssociation &entry = j->second;// 获取到valuevalue = entry.value();policy = entry.policy();if (policy & OBJC_ASSOCIATION_GETTER_RETAIN) {objc_retain(value);}}}}if (value && (policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE)) {objc_autorelease(value);}// 返回关联对像的值return value;
}

也是差不多的

首先有一个manager 然后里面有一个HashMap
然后通过key 找到了对应的Map
再通过索引获取到关联对象
然后通过两个entry方法获取到了关联对象里面存的value跟policy
然后返回了关联对象的值value

注意
关联对象并不是存储在被关联对象本身内存中
关联对象存储在全局的统一的一个manager中

_object_remove_assocations

void _object_remove_assocations(id object) {// 声明了一个vectorvector< ObjcAssociation,ObjcAllocator<ObjcAssociation> > elements;{AssociationsManager manager;AssociationsHashMap &associations(manager.associations());// 如果map size为空,直接返回if (associations.size() == 0) return;// 获取对象的DISGUISE值disguised_ptr_t disguised_object = DISGUISE(object);AssociationsHashMap::iterator i = associations.find(disguised_object);if (i != associations.end()) {// copy all of the associations that need to be removed.ObjectAssociationMap *refs = i->second;for (ObjectAssociationMap::iterator j = refs->begin(), end = refs->end(); j != end; ++j) {elements.push_back(j->second);}// remove the secondary table.delete refs;associations.erase(i);}}// the calls to releaseValue() happen outside of the lock.for_each(elements.begin(), elements.end(), ReleaseValue());
}

整体逻辑就是 通过传入的id object找到对应的Map
然后直接删除掉这个Map
那不就没有后面的所有的关联对象了嘛 就直接移除了所有的这个id object的关联对象

如果你只希望移除其中的一个关联对象的话
那给你想移除的关联对象赋值nil就好了

iOS 关联对象的实现原理相关推荐

  1. 关联对象的实现原理【OC】

    前言 AssociationedObject多用于在Category中为特定类扩展成员变量,也有用于在运行时为某些对象动态创建成员变量.AssociationedObject可以说是一种特殊的成员变量 ...

  2. 【iOS开发】——Category底层原理、Extension、关联对象

    [iOS开发]--Category底层原理.Extension.关联对象 Category是什么?它可以用来干什么? Category特点 Category的实质以及实现过程 Category结构体 ...

  3. iOS Category 添加属性实现原理 - 关联对象

    iOS Category 添加属性实现原理 - 关联对象 RunTime为Category动态关联对象 使用RunTime给系统的类添加属性,首先需要了解对象与属性的关系.对象一开始初始化的时候其属性 ...

  4. 【iOS】——分类、扩展和关联对象

    目录 一.分类Category和扩展Extension 1.分类Category(运行期) 2.扩展Extension(编译期) 3.分类和扩展的区别 二.分类Category的实质 1.分类的结构 ...

  5. [iOS]-Category、Extension和关联对象

    目录: 参考的博客: 前言 一.Category分类 Extension扩展 Category的实质 Category结构体 将分类转成C++看起 对象方法列表结构体 类方法列表结构体 协议列表结构体 ...

  6. [iOS开发]Category、Extension和关联对象

    文章目录 什么是Category? 分类和扩展 Category的实质 Category结构体 从C++开始看起 对象方法列表结构体 类方法列表结构体 协议列表结构体 属性列表结构体 category ...

  7. 视频教程-iOS底层原理班(下)/OC对象/关联对象/多线程/内存管理/性能优化-iOS

    iOS底层原理班(下)/OC对象/关联对象/多线程/内存管理/性能优化 小码哥教育CEO,曾开发了2个iOS的流行开源框架(MJRefresh.MJExtension),目前在国内的使用率非常高. 李 ...

  8. iOS底层原理班(下)/OC对象/关联对象/多线程/内存管理/性能优化-李明杰-专题视频课程...

    iOS底层原理班(下)/OC对象/关联对象/多线程/内存管理/性能优化-236人已学习 课程介绍         得遇名师,突飞猛进!iOS培训王者MJ(李明杰)老师精心研发,iOS进阶课程,实用技术 ...

  9. 【iOS高级资深工程师面试篇】②、2022年,金九银十我为你准备了《iOS高级资深工程师面试知识总结》 Objective-C语言特性部分1/2 分类-关联对象-扩展-代理

    iOS高级资深工程师面试篇系列 - 已更新3篇 UI部分1/3 -UITableView-事件传递&视图响应 UI部分2/3 -图像显示原理-UI卡顿&掉帧 UI部分3/3 -UIVi ...

最新文章

  1. Django使用数据库(Mariadb/Mysql)
  2. oracle form 头行合计,FORM主从块头行金额汇总
  3. IOS 程序内调用本地打电话功能-make a phone call
  4. C# 调用cmd执行指令
  5. 世界上最稳定的系统——维稳就靠它了!
  6. Lua中的模块和使用
  7. 计算机网络安全管理协议,河西学院校园网络安全管理协议
  8. 【干货】腾讯内部-微信视频号介绍、商业玩法及涨粉方案.pdf(附下载链接)...
  9. Mac底下java和jre路径
  10. 五步法”判断自媒体创作好与坏的标准
  11. 光模块自动测试系统软件,可插拔收发光模块自动化检测系统
  12. vant制作首页的加载中和暂无数据
  13. 编译libpng和zlib
  14. 用netbean搭建第一个struts的web项目
  15. 蛋白质组学技术与药物作用新靶点研究进展
  16. 企业借力OA系统打造数字档案馆,提高档案管理、查阅效率
  17. 服务器监控系统应有哪些功能,公安视频监控系统需要具备哪些功能
  18. windows局域网的一个经典的入侵方法
  19. 论文阅读:Generating Talking Face Landmarks from Speech
  20. 首旅如家:夜间赏樱、采茶旅拍和登高许愿成为2021打开春天的新方式

热门文章

  1. 详解拦截器和过滤器的区别
  2. 【R】【密度聚类、层次聚类、期望最大化聚类】
  3. Java计算机毕业设计甜趣网上蛋糕店订购系统源码+系统+数据库+lw文档
  4. 数据结构与算法-Part6——数组与广义表
  5. [乐意黎翻译]JavaScript命令之Console大全
  6. Ubuntu系统---安装搜狗输入法
  7. day24/MyIE.java
  8. 在互联网公司大举进军移动支付和网络理财之际 平安推出壹钱包对战互联网“群狼”
  9. wsl安装ubuntu并设置gnome图形界面详细步骤(win11+ubuntu18)
  10. 6 款国外开源web oa办公系统