前言
我们都很清楚本身分类使用@Property添加属性只会有setter和getter方法的声明没有对应的实现,也不会有对应的带下划线的成员变量存在,目前我们添加属性都是通过关联对象的方式即运用了运行时相关的API来实现的,下面我们来分析下它的实现原理。
关联对象添加
我们常写的分类添加关联对象的代码如下:
- (void)setCate_name:(NSString *)cate_name{
/**
1: 对象
2: 标识符
3: value
4: 策略
*/
objc_setAssociatedObject(self, "cate_name", cate_name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)cate_name{
return objc_getAssociatedObject(self, "cate_name");
}
复制代码
我们进入objc_setAssociatedObject()
方法内部,看下它的实现:
进入_object_set_associative_reference
方法:
void _object_set_associative_reference(id object, const void *key, id value, uintptr_t policy)
{
// This code used to work when nil was passed for object and key. Some code
// probably relies on that to not crash. Check and handle it explicitly.
// rdar://problem/44094390
if (!object && !value) return;
if (object->getIsa()->forbidsAssociatedObjects())
_objc_fatal("objc_setAssociatedObject called on instance (%p) of class %s which does not allow associated objects", object, object_getClassName(object));
//对object指针进行数据封装以方便作为统一的格式处理
DisguisedPtr<objc_object> disguised{(objc_object *)object};
//对存储策略和value进行相关赋值存储在ObjcAssociation对象中
ObjcAssociation association{policy, value};
// retain the new value (if any) outside the lock.
association.acquireValue();
bool isFirstAssociation = false;
{
//构造函数 AssociationsManager() { AssociationsManagerLock.lock(); }
//析构函数 ~AssociationsManager() { AssociationsManagerLock.unlock();}
//下面这行代码调用AssociationsManager构造函数
AssociationsManager manager;
//这里AssociationsHashMap是个全局静态变量,相当于单例,获取全局唯一的哈希表
AssociationsHashMap &associations(manager.get());
//value存在
if (value) {
auto refs_result = associations.try_emplace(disguised, ObjectAssociationMap{});
if (refs_result.second) {
/* it's the first association we make */
isFirstAssociation = true;
}
/* establish or replace the association */
auto &refs = refs_result.first->second;
auto result = refs.try_emplace(key, std::move(association));
if (!result.second) {
association.swap(result.first->second);
}
} else {
//若value不存在,移除关联对象
auto refs_it = associations.find(disguised);
if (refs_it != associations.end()) {
auto &refs = refs_it->second;
auto it = refs.find(key);
if (it != refs.end()) {
association.swap(it->second);
refs.erase(it);
if (refs.size() == 0) {
associations.erase(refs_it);
}
}
}
}
}
// Call setHasAssociatedObjects outside the lock, since this
// will call the object's _noteAssociatedObjects method if it
// has one, and this may trigger +initialize which might do
// arbitrary stuff, including setting more associated objects.
if (isFirstAssociation)
object->setHasAssociatedObjects();
// release the old value (outside of the lock).
association.releaseHeldValue();
}
复制代码
简单的地方我们直接就上面代码里注释说明,这里有涉及哈希表(AssociationsHashMap
)存储查询的地方associations-(AssociationsHashMap).try_emplace(disguised, ObjectAssociationMap{})
,我们看下:
这里我们首先看到BucketT *TheBucket
创建了一个空的桶子,接着调用LookupBucketFor(Key, TheBucket)
判断根据Key
查询对应的BucketT
是否存在,若存在,调用make_pair
返回当前BucketT
并返回false
,表示未添加新的BucketT
,这里的Key
是外面传入的object指针
包装的DisguisedPtr<objc_object> disguised
,Value
是外部传入的ObjectAssociationMap
,我们看下LookupBucketFor
方法:
这里LookupBucketFor
调用了另一个方法LookupBucketFor(const LookupKeyT &Val, const BucketT *&FoundBucket)
:
这里其实就是哈希表的一个查询过程,查询返回结果后,就接着根据返回的结果来继续执行后续代码,我们第一次进来调用LookupBucketFor
的时候,bucket
肯定是空的,所以接着执行InsertIntoBucket(TheBucket, Key, std::forward<Ts>(Args)...)
,把当前创建的空桶子存入AssociationsHashMap
中,此时注意BucketT
是空的,我们调试下:
我们看下InsertIntoBucket
实现:
我们看到这里一个关键函数InsertIntoBucketImpl(Key, Key, TheBucket)
:
这里插入完成后,我们在InsertIntoBucket
返回的TheBucket
就看到它就赋值完成:
此时我们第一次调用associations.try_emplace
调用结束,我们看refs_result.second
为true
,此时isFirstAssociation
也为true
,此时我们的policy
和value
还没有存进去,他们目前存在assocition
对象中,我们看下refs
这里,此时都还是空:
下一步refs.try_emplace(key,std::move(association))
存入association
,这里刚好也解释了第一次associations-(AssociationsHashMap).try_emplace(disguised, ObjectAssociationMap{})
中bucket
最终插入后并不包含相关的Key
和Value
,因为此时association
并没有传入,我们看refs.try_emplace
这里,此时传入的Key
就是我们外部传入的Key
(这里是const void *
类型值为cate_name
),而Key
存bucket
的first
,value
存second
:
我们看refs.try_emplace
调用结束后,发现bucket
中Key
和Value
都已存入:
到这里,我们基本已经清楚了它的存储结构,大致是这样的,我们看张图:
关联对象设值流程
- 创建一个 AssociationsManager 管理类
- 获取唯一的全局静态哈希Map
- 判断是否插入的关联值是否存在:
3.1 存在走第4步
3.2 不存在:关联对象插入空流程 - 创建一个空的 ObjectAssociationMap 去取查询的键值对
- 如果发现没有这个key就插入一个空的BucketT进去,然后返回
- 标记对象存在关联对象
- 用当前修饰策略(policy)和值(value)组成了一个ObjcAssociation替换原来BucketT中的空
- 标记一下 ObjectAssociationMap的第一次为false
关联对象取值分析
我们上面分析的是objc_setAssociatedObject
,接下来看下objc_getAssociatedObject(self, "cate_name")
,我们进入底层源码看下它的具体实现:
_object_get_associative_reference(id object, const void *key)
{
ObjcAssociation association{};
{
//创建一个 AssociationsManager管理类
AssociationsManager manager;
//获取唯一的全局静态AssociationsHashMap
AssociationsHashMap &associations(manager.get());
//根据 DisguisedPtr找到AssociationsHashMap中的iterator迭代查询器
AssociationsHashMap::iterator i = associations.find((objc_object *)object);
//如果这个迭代查询器不是最后一个获取 :ObjectAssociationMap (这里有策略和value)
if (i != associations.end()) {
ObjectAssociationMap &refs = i->second;
ObjectAssociationMap::iterator j = refs.find(key);
//找到ObjectAssociationMap的迭代查询器获取一个经过属性修饰符修饰的value
if (j != refs.end()) {
association = j->second;
association.retainReturnedValue();
}
}
}
//返回value
return association.autoreleaseReturnedValue();
}
复制代码
- 创建一个 AssociationsManager 管理类
- 获取唯一的全局静态哈希表AssociationsHashMap
- 根据DisguisedPtr找到AssociationsHashMap中的 iterator迭代查询器
- 如果这个迭代查询器不是最后一个 获取:ObjectAssociationMap(这里有policy和value)
- 找到ObjectAssociationMap的迭代查询器获取一个经过属性修饰符修饰的value
- 返回value
关联对象移除
关联对象的移除在dealloc
方法中,我们看下它调用流程:dealloc——>_objc_rootDealloc——>rootDealloc
:
我们接着看object_dispose()——>objc_destructInstance
:
我们进入_object_remove_assocations
,看下它的实现:
相关面试题分析
[self class]和[super class]的区别
[self class]
本质是objc_msgSend
,消息的接收者是self
,方法编号:class
,[super class]
本质是objc_msgSendSuper
,消息的接收者也是self
,方法编号:class
;我们看个例子:
我们运行之后这里输出得到的都是LGTeacher,那么为什么呢?首先LGTeacher中是没有class
这个方法的,该方法存在于NSObject
中,我们可以进入class
方法看下:
我们之前就已经知道方法调用本质是消息的发送,底层调用的objc_msgSend()
,这里的class
方法是有两个默认参数的id self
和 sel _cmd
,只是被系统隐藏起来了,我们要清楚这里的self
,就是传进来的实例对象,而object_getClass()
内存实现是这样的:
obj——>getIsa()
获取的就是当前类,因为我们之前已经讲过实例对象的isa
指向当前类,所以获取的LGTeacher
;[super class]
这里的super
只是个关键字,跟前面self
不同,self
是形参名,我们汇编调试:
我们可以看到[super class]
底层调用的objc_msgSendSuper2
,我们源码中看下它:
这里我看注释就可以知道super
使用的当前类,而不是它的父类,我们看下这里的objc_super
的数据结构:
从上图中可以看出,这里recevier
接收的是当前类的实例,最后一句注释我们可以知道[super class]
其实只是保证了当前父类被作为第一个查询的类,所以到这里我们基本也就清楚了[super class]
为什么跟[self class]
输出同一个类,因为他们最终接收到的都是当前类的实例,通过实例对象找到当前类。这里objc_msgSendSuper2
本质也是从objc_msgSuperSend()
跳转过来的,我们在汇编代码可以看到:
ENTRY _objc_msgSendSuper
UNWIND _objc_msgSendSuper, NoFrame
ldp p0, p16, [x0] // p0 = real receiver, p16 = class
//这里做了objc_msgSendSuper2的跳转
b L_objc_msgSendSuper2_body
END_ENTRY _objc_msgSendSuper
复制代码