iOS底层笔记--关联对象

217 阅读3分钟

前言

本文属笔记性质,是对kirito_song冰凌天两位文章学习和转载

文章具体地址:
MJiOS底层笔记--Cateogry
小码哥iOS学习笔记第七天: 关联对象

关联对象

由一个manager(AssociationsManager)通过二维map(AssociationsHashMap)统一管理

实现原理

  1. AssociationsHashMapobject作为key,另一个map(ObjectAssociationMap)作为value
  2. ObjectAssociationMapkey(const void *类型)作为key,具体的valuepolicy作为value

所以实际上objc_setAssociatedObject关联对象并不会改变类的状态以及实例的分布。

关联成员变量, 有四种key的取值方式

  1. char *做为key
  • 关联对象方法中, key的类型是const void *, 所以传入一个char *类型的数据就可以了
  1. void *做为key
  2. 字符串字面量做为key
  • OC中字符串字面量存放在内存中的常量区, 所以相同字符串字面量的地址是相同的
  1. @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对应修饰符assign
    • OBJC_ASSOCIATION_RETAIN_NONATOMIC对应修饰符strong,nonatomic
    • OBJC_ASSOCIATION_COPY_NONATOMIC对应修饰符copy, nonatomic
    • OBJC_ASSOCIATION_RETAIN对应修饰符strong, atomic
    • OBJC_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;
}

参考资料
blog.csdn.net/weixin_3405…