前言
本文属笔记性质,是对kirito_song和冰凌天两位文章学习和转载
文章具体地址:
MJiOS底层笔记--Cateogry
小码哥iOS学习笔记第七天: 关联对象
关联对象
由一个manager(AssociationsManager)通过二维map(AssociationsHashMap)统一管理
实现原理

AssociationsHashMap以object作为key,另一个map(ObjectAssociationMap)作为value。ObjectAssociationMap以key(const void *类型)作为key,具体的value和policy作为value。
所以实际上objc_setAssociatedObject关联对象并不会改变类的状态以及实例的分布。
关联成员变量, 有四种key的取值方式
char *做为key值
- 关联对象方法中,
key的类型是const void *, 所以传入一个char *类型的数据就可以了
void *做为key值
- 字符串字面量做为
key值
OC中字符串字面量存放在内存中的常量区, 所以相同字符串字面量的地址是相同的
@selecter(方法名)做为key值
- 同一个方法的
@selector(方法名), 地址是相同的, 所以可以直接作为关联对象的key值
OC中提供了一个关键字_cmd, 它的值就是当前方法的方法名, 所以上面的代码中, 可以如下修改- 在
-(void)setName:(NSString *)name方法中,_cmd的值就是@selector(setName:), 在-(NSString *)name中,_cmd的值就是@selector(name)
set方法
void objc_setAssociatedObject(id object, const void * key,
id value,
objc_AssociationPolicy policy)
我们调用此方法的时候,一共传递了四个参数:
| 参数名称 | 解释 | ||||
|---|---|---|---|---|---|
| id object | 需要关联的对象 | ||||
| void *key | 对应的key,要求唯一 | ||||
| id value | 对应的值 | ||||
| objc_AssociationPolicy policy | 内存管理策略 |
- objc_AssociationPolicy
OBJC_ASSOCIATION_ASSIGN对应修饰符assignOBJC_ASSOCIATION_RETAIN_NONATOMIC对应修饰符strong,nonatomicOBJC_ASSOCIATION_COPY_NONATOMIC对应修饰符copy, nonatomicOBJC_ASSOCIATION_RETAIN对应修饰符strong, atomicOBJC_ASSOCIATION_COPY对应修饰符copy, atomic
真正的实现函数_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;
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.associations());
disguised_ptr_t disguised_object = DISGUISE(object);//// 取反object 地址 作为accociative key
if (new_value) {
// break any existing association.
AssociationsHashMap::iterator i = associations.find(disguised_object);//首先通过地址获取对象的hashmap
if (i != associations.end()) {//判断是否已经存在,已经存在
// secondary table exists
ObjectAssociationMap *refs = i->second;//取值,对应的map
ObjectAssociationMap::iterator j = refs->find(key);//通过key查找
if (j != refs->end()) {//如果已经存在
old_association = j->second;//取到原来老的值,以便后边对其释放
j->second = ObjcAssociation(policy, new_value);//存储新的值
} else {//不存在
(*refs)[key] = ObjcAssociation(policy, new_value);
}
} else {//如果不存在,创建一个
// create the new association (first time).
ObjectAssociationMap *refs = new ObjectAssociationMap;
associations[disguised_object] = refs;
(*refs)[key] = ObjcAssociation(policy, new_value);
object->setHasAssociatedObjects(); //将object标记为 has AssociatedObjects
}
} else {//不存在则创建一个
// setting the association to nil breaks the association.
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);
}
}
}
}
// release the old value (outside of the lock).
//释放掉old关联对象。(如果多次设置同一个key的value,这里会释放之前的value)
if (old_association.hasValue()) ReleaseValue()(old_association);
}
get方法
取值的过程其实就比较简单了,就是从一个hashmap中取值
id objc_getAssociatedObject(id object, const void *key) {
return _object_get_associative_reference(object, (void *)key);
}
真正的实现函数_object_get_associative_reference
id _object_get_associative_reference(id object, void *key) {
id value = nil;
uintptr_t policy = OBJC_ASSOCIATION_ASSIGN;
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.associations());
disguised_ptr_t disguised_object = DISGUISE(object);
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()) {
ObjcAssociation &entry = j->second;
value = 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;
}