iOS 关联对象的实现原理
文章目录
- 前言
- 一般使用
- 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_type
和mapped_type
作为key
和value
如果传值的话 是以前面两个参数作为key
和value
那现在就明白 在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); }};
根据上面推测 这次就可以知道
在ObjectAssociationMap
中void *
作为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 关联对象的实现原理相关推荐
- 关联对象的实现原理【OC】
前言 AssociationedObject多用于在Category中为特定类扩展成员变量,也有用于在运行时为某些对象动态创建成员变量.AssociationedObject可以说是一种特殊的成员变量 ...
- 【iOS开发】——Category底层原理、Extension、关联对象
[iOS开发]--Category底层原理.Extension.关联对象 Category是什么?它可以用来干什么? Category特点 Category的实质以及实现过程 Category结构体 ...
- iOS Category 添加属性实现原理 - 关联对象
iOS Category 添加属性实现原理 - 关联对象 RunTime为Category动态关联对象 使用RunTime给系统的类添加属性,首先需要了解对象与属性的关系.对象一开始初始化的时候其属性 ...
- 【iOS】——分类、扩展和关联对象
目录 一.分类Category和扩展Extension 1.分类Category(运行期) 2.扩展Extension(编译期) 3.分类和扩展的区别 二.分类Category的实质 1.分类的结构 ...
- [iOS]-Category、Extension和关联对象
目录: 参考的博客: 前言 一.Category分类 Extension扩展 Category的实质 Category结构体 将分类转成C++看起 对象方法列表结构体 类方法列表结构体 协议列表结构体 ...
- [iOS开发]Category、Extension和关联对象
文章目录 什么是Category? 分类和扩展 Category的实质 Category结构体 从C++开始看起 对象方法列表结构体 类方法列表结构体 协议列表结构体 属性列表结构体 category ...
- 视频教程-iOS底层原理班(下)/OC对象/关联对象/多线程/内存管理/性能优化-iOS
iOS底层原理班(下)/OC对象/关联对象/多线程/内存管理/性能优化 小码哥教育CEO,曾开发了2个iOS的流行开源框架(MJRefresh.MJExtension),目前在国内的使用率非常高. 李 ...
- iOS底层原理班(下)/OC对象/关联对象/多线程/内存管理/性能优化-李明杰-专题视频课程...
iOS底层原理班(下)/OC对象/关联对象/多线程/内存管理/性能优化-236人已学习 课程介绍 得遇名师,突飞猛进!iOS培训王者MJ(李明杰)老师精心研发,iOS进阶课程,实用技术 ...
- 【iOS高级资深工程师面试篇】②、2022年,金九银十我为你准备了《iOS高级资深工程师面试知识总结》 Objective-C语言特性部分1/2 分类-关联对象-扩展-代理
iOS高级资深工程师面试篇系列 - 已更新3篇 UI部分1/3 -UITableView-事件传递&视图响应 UI部分2/3 -图像显示原理-UI卡顿&掉帧 UI部分3/3 -UIVi ...
最新文章
- Django使用数据库(Mariadb/Mysql)
- oracle form 头行合计,FORM主从块头行金额汇总
- IOS 程序内调用本地打电话功能-make a phone call
- C# 调用cmd执行指令
- 世界上最稳定的系统——维稳就靠它了!
- Lua中的模块和使用
- 计算机网络安全管理协议,河西学院校园网络安全管理协议
- 【干货】腾讯内部-微信视频号介绍、商业玩法及涨粉方案.pdf(附下载链接)...
- Mac底下java和jre路径
- 五步法”判断自媒体创作好与坏的标准
- 光模块自动测试系统软件,可插拔收发光模块自动化检测系统
- vant制作首页的加载中和暂无数据
- 编译libpng和zlib
- 用netbean搭建第一个struts的web项目
- 蛋白质组学技术与药物作用新靶点研究进展
- 企业借力OA系统打造数字档案馆,提高档案管理、查阅效率
- 服务器监控系统应有哪些功能,公安视频监控系统需要具备哪些功能
- windows局域网的一个经典的入侵方法
- 论文阅读:Generating Talking Face Landmarks from Speech
- 首旅如家:夜间赏樱、采茶旅拍和登高许愿成为2021打开春天的新方式
热门文章
- 详解拦截器和过滤器的区别
- 【R】【密度聚类、层次聚类、期望最大化聚类】
- Java计算机毕业设计甜趣网上蛋糕店订购系统源码+系统+数据库+lw文档
- 数据结构与算法-Part6——数组与广义表
- [乐意黎翻译]JavaScript命令之Console大全
- Ubuntu系统---安装搜狗输入法
- day24/MyIE.java
- 在互联网公司大举进军移动支付和网络理财之际 平安推出壹钱包对战互联网“群狼”
- wsl安装ubuntu并设置gnome图形界面详细步骤(win11+ubuntu18)
- 6 款国外开源web oa办公系统