美文网首页
iOS的runtime:关联对象是如何实现的?objc_setA

iOS的runtime:关联对象是如何实现的?objc_setA

作者: jlstmac | 来源:发表于2018-08-14 11:26 被阅读61次

iOS的一个重要特性:runtime中一个重要功能就是关联对象。作用就是运行时动态的给对象添加变量。
那么runtime是如何实现动态添加的呢?直接上结论
关联对象并没有存放在对象的实体中,而是runtime维护了一个全局二维map来管理所有关联对象。
如果你对iOS实现过程不敢兴趣,那么你可以关闭这一篇文章了。但是如果你想了解可以继续看下去
来看看runtime源码,objc_setAssociatedObject的具体实现

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);
        if (new_value) {
            // break any existing association.
            AssociationsHashMap::iterator i = associations.find(disguised_object);
            if (i != associations.end()) {
                // secondary table exists
                ObjectAssociationMap *refs = i->second;
                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 {
                // create the new association (first time).
                ObjectAssociationMap *refs = new ObjectAssociationMap;
                associations[disguised_object] = refs;
                (*refs)[key] = ObjcAssociation(policy, new_value);
                object->setHasAssociatedObjects();
            }
        } 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).
    if (old_association.hasValue()) ReleaseValue()(old_association);
}

函数中涉及几个4个重要的数据结构:
AssociationsManager //管理全局AssociationsHashMap
AssociationsHashMap //存放对象的关联对象map的map(key为传入的object,value为map,也就是ObjectAssociationMap)
ObjectAssociationMap //存放关联对象的map(key为传入的key,value为关联对象)
ObjcAssociation //关联对象实体包含了value和policy两个重要信息(policy决定了value的内存管理方式)
文字介绍可能比较绕,引用一下另一位间书大神的文章的图
https://www.jianshu.com/p/4b463169a84a

5796542-048198c289ac366b.png

四个结构图关系一目了然。所以:
runtime中维护了一个全局二维map来管理关联对象。
runtime除了还有一个objc_getAssociatedObject在这个二维map中取出关联对象外,还有一个objc_removeAssociatedObjects方法来移除某一个对象的所有关联对象。来看看源码:

void _object_remove_assocations(id object) {
    vector< ObjcAssociation,ObjcAllocator<ObjcAssociation> > elements;
    {
        AssociationsManager manager;
        AssociationsHashMap &associations(manager.associations());
        if (associations.size() == 0) return;
        disguised_ptr_t disguised_object = DISGUISE(object);
        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;
            for (ObjectAssociationMap::iterator j = refs->begin(), end = refs->end(); j != end; ++j) {
                elements.push_back(j->second);
            }
            // remove the secondary table.
            delete refs;
            associations.erase(i);
        }
    }
    // the calls to releaseValue() happen outside of the lock.
    for_each(elements.begin(), elements.end(), ReleaseValue());
}

从源码可以看出也就是移除了对象的ObjectAssociationMap这个二级map。

引用:https://www.jianshu.com/p/4b463169a84a

相关文章

网友评论

      本文标题:iOS的runtime:关联对象是如何实现的?objc_setA

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