前言
关联对象(AssociatedObject)是一种运用runtime在分类中添加"属性"的方法。那么关联是怎样实现添加"属性"的呢?
关联对象的实现步骤
关联对象的实现步骤如下:
- 在分类中.h中声明一个属性
- 在.m中声明该属性为动态加载 @dynamic
- 实现setter和getter方法
1.
@interface UIViewController (Test)
@property (nonatomic, copy) NSString *es_title;
@end
@implementation UIViewController (Test)
2.
@dynamic es_title;
3.
- (void)setEs_title:(NSString *)es_title {
objc_setAssociatedObject(self, &kTestProperty, es_title, OBJC_ASSOCIATION_COPY);
}
- (NSString *)es_title {
return objc_getAssociatedObject(self, &kTestProperty);
}
@end
关联对象分析
实际上从上代码能看出,实际上关联对象并没有往原有类中添加成员变量。当然了,实际上类的空间编译的时候就已经是确定好的了,分类的属性实际上就是set和get方法的实现。
那么我们只要看懂set和get方法内部的实现就能搞清楚究竟关联对象是怎么做的。
源码分析
objc_setAssociatedObject
老规矩,看下runtime源码里面是怎么做的
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 old_association(0, nil);
id new_value = value ? acquireValue(value, policy) : nil;
{
//初始化manager,全局管理类
AssociationsManager manager;
//获取管理的hashmap
AssociationsHashMap &associations(manager.associations());
//对象地址按位取反作为地址key
disguised_ptr_t disguised_object = DISGUISE(object);
//存在关联值
if (new_value) {
//根据地址key在hashmap中找到存储的对象关联表(ObjectAssociationMap)
AssociationsHashMap::iterator i = associations.find(disguised_object);
//若是能找到对象关联表的话
if (i != associations.end()) {
//获取找到的对象关联表(ObjectAssociationMap)
ObjectAssociationMap *refs = i->second;
//在对象关联表中根据key查找关联对象(ObjcAssociation)
ObjectAssociationMap::iterator j = refs->find(key);
//若能找到关联对象
if (j != refs->end()) {
//存储过去的关联对象
old_association = j->second;
//将对象关联表中的关联对象重新赋值
j->second = ObjcAssociation(policy, new_value);
}
//找不到关联对象
else {
//根据指针key直接添加新的关联对象
(*refs)[key] = ObjcAssociation(policy, new_value);
}
}
//若是找不到的话
else {
//创建新的对象关联map
ObjectAssociationMap *refs = new ObjectAssociationMap;
//在全局管理的hashmap中添加对象关联map
associations[disguised_object] = refs;
//对象关联map添加新的关联对象
(*refs)[key] = ObjcAssociation(policy, new_value);
//标记对象存在关联对象
object->setHasAssociatedObjects();
}
}
//不存在关联值就擦除
else {
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;
refs->erase(j); //擦除
}
}
}
}
// 若是存在旧的关联对象,则将旧的关联对象释放
if (old_association.hasValue()) ReleaseValue()(old_association);
}
上面的代码把每个步骤所做的内容都解释了一遍,从头往下看的话应该能看出来。
总结
关联对象的实质是在全局管理一个map,然后用对象按位取反来作为key来存储。用一张图来说明的话,可能更加清晰
关联对象
- 通过AssociationsManager管理一个hashmap
- hashmap中用DISGUISE(obj)存储一个关联对象map(ObjetAssociationMap)
- 关联对象map中根据用户自定义的key存储关联对象
- 关联对象中包含关联策略和key