美文网首页
OC底层原理(六):关联对象

OC底层原理(六):关联对象

作者: TheEnded | 来源:发表于2019-05-31 11:45 被阅读0次

之前我们分析过了Category本质,我们都知道Category不能直接添加成员变量,因为其内部结构没有存放成员变量的结构。
为了达到和正常类使用属性一样的效果,我们一般用runtime关联对象的方法来实现。

关联对象有这几个API
///获取某个对象的关联属性
id objc_getAssociatedObject(id object, const void *key)
///给某个对象添加关联属性
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
///移除对象所有的关联属性
void objc_removeAssociatedObjects(id object) 

怎么使用不过多介绍了,直接分析内部实现原理。
在这些方法的实现里,有几个关键对象
AssociationsManager
AssociationsHashMap
ObjectAssociationMap
ObjcAssociation
先看下AssociationsManager

image.png
再看AssociationsHashMap
image.png
AssociationsHashMap是一个hashMap,和字典很像。继承unordered_map,这里是一个泛型。

再看ObjectAssociationMap


image.png

然后是ObjcAssociation


image.png
这几个对象的关系图,通过关系图应该比较好理解源码
image.png
  1. 然后我们直接分析源码,先看objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
    里面是直接调用了_object_set_associative_reference(object, (void *)key, value, policy)
    这里把注释都写代码里面了
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());
        //通过object经过运算得到disguised_object
        disguised_ptr_t disguised_object = DISGUISE(object);
        //new_value代表是否有传值
        if (new_value) {
            // break any existing association.
            //通过disguised_object找到i,i->second得到ObjectAssociationMap
            AssociationsHashMap::iterator i = associations.find(disguised_object);
            if (i != associations.end()) {
                // secondary table exists
                ObjectAssociationMap *refs = i->second;
                //通过key找到j,j->second得到ObjcAssociation(old_association),设置值
                //如果没有找到j,设置新关联对象
                ObjectAssociationMap::iterator j = refs->find(key);
                if (j != refs->end()) {
                    old_association = j->second;
                    j->second = ObjcAssociation(policy, new_value);
                } else {
                    (*refs)[key] = ObjcAssociation(policy, new_value);
                }
            } else {
                //如果上面i没有找到ObjectAssociationMap,说明这个对象没有设置过关联对象,就new一个ObjectAssociationMap对象,再进行关联对象
                // create the new association (first time).
                ObjectAssociationMap *refs = new ObjectAssociationMap;
                associations[disguised_object] = refs;
                (*refs)[key] = ObjcAssociation(policy, new_value);
                object->setHasAssociatedObjects();
            }
        } else {
            //如果参数value为nil,就走这里关系对象在这里被移除,refs->erase(j)
            // 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).
    if (old_association.hasValue()) ReleaseValue()(old_association);
}
  1. 再看objc_getAssociatedObject(id object, const void *key)里面调用了_object_get_associative_reference(object, (void *)key)
    还是直接上源码
id _object_get_associative_reference(id object, void *key) {
    id value = nil;
    uintptr_t policy = OBJC_ASSOCIATION_ASSIGN;
    {
        AssociationsManager manager;
        AssociationsHashMap &associations(manager.associations());
        //通过object运算得到disguised_object
        disguised_ptr_t disguised_object = DISGUISE(object);
        //disguised_object得到i,i->second得到ObjectAssociationMap *refs
        AssociationsHashMap::iterator i = associations.find(disguised_object);
        if (i != associations.end()) {
            ObjectAssociationMap *refs = i->second;
            //refs里通过key找j,j->second得到ObjcAssociation &entry,
            //entry内部的value就是得到的value,policy就是关联对象策略
            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;
}
  1. 然后看objc_removeAssociatedObjects(id object)里面调用了_object_remove_assocations
void _object_remove_assocations(id object) {
    vector< ObjcAssociation,ObjcAllocator<ObjcAssociation> > elements;
    {
        AssociationsManager manager;
        AssociationsHashMap &associations(manager.associations());
        if (associations.size() == 0) return;
        //得到disguised_object
        disguised_ptr_t disguised_object = DISGUISE(object);
        //通过disguised_object获得i,i->second得到ObjectAssociationMap *refs
        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;
            //遍历ref获得j,移除j->second(关联对象)。等于是把关联对象的hashMap先清空,然后将对象的这个hasMap从全局的AssociationsHashMap中移除。
            for (ObjectAssociationMap::iterator j = refs->begin(), end = refs->end(); j != end; ++j) {
                elements.push_back(j->second);
            }
            // remove the secondary table.
            //移除了refs
            delete refs;
            associations.erase(i);
        }
    }
    // the calls to releaseValue() happen outside of the lock.
    for_each(elements.begin(), elements.end(), ReleaseValue());
}

最后总结:

关联对象并不是存储在被关联对象本身的内存中
关联对象是存储在全局统一的一个AssociationsManager中
设置关联对象value传nil,等于移除关联对象。

相关文章

网友评论

      本文标题:OC底层原理(六):关联对象

      本文链接:https://www.haomeiwen.com/subject/zqfwtctx.html