美文网首页
关联函数的实现原理

关联函数的实现原理

作者: Crazy2015 | 来源:发表于2019-07-11 18:49 被阅读0次

    其中的几个类和数据结构,具体分析了解其中它们的作用:

    AssociationsManager
    AssociationsHashMap

    spinlock_t AssociationsManagerLock;
    
    class AssociationsManager {
        // associative references: object pointer -> PtrPtrHashMap.
        static AssociationsHashMap *_map;
    public:
        AssociationsManager()   { AssociationsManagerLock.lock(); }
        ~AssociationsManager()  { AssociationsManagerLock.unlock(); }
        
        AssociationsHashMap &associations() {
            if (_map == NULL)
                _map = new AssociationsHashMap();
            return *_map;
        }
    };
    
    AssociationsHashMap *AssociationsManager::_map = NULL;
    

    它维护了 spinlock_tAssociationsHashMap 的单例,初始化它的时候会调用 lock.lock() 方法,在析构时会调用 lock.unlock(),而 associations 方法用于取得一个全局的 AssociationsHashMap 单例。

    也就是说 AssociationsManager 通过持有一个自旋锁 spinlock_t 保证对 AssociationsHashMap 的操作是线程安全的,即每次只会有一个线程对 AssociationsHashMap 进行操作

    首先,AssociationsHashMap 用与保存从对象的 disguised_ptr_t 到 ObjectAssociationMap 的映射:

    class AssociationsHashMap : public unordered_map<disguised_ptr_t, ObjectAssociationMap *, DisguisedPointerHash, DisguisedPointerEqual, AssociationsHashMapAllocator> {
    public:
        void *operator new(size_t n) { return ::malloc(n); }
        void operator delete(void *ptr) { ::free(ptr); }
    };
    
    

    而 ObjectAssociationMap 则保存了从 key 到关联对象 ObjcAssociation 的映射,这个数据结构保存了当前对象对应的所有关联对象:

    ObjectAssociationMap

    class ObjectAssociationMap : public std::map<void *, ObjcAssociation, ObjectPointerLess, ObjectAssociationMapAllocator> {
    public:
       void *operator new(size_t n) { return ::malloc(n); }
       void operator delete(void *ptr) { ::free(ptr); }
    };
    

    ObjcAssociation

    class ObjcAssociation {
            uintptr_t _policy;
            id _value;
        public:
            ObjcAssociation(uintptr_t policy, id value) : _policy(policy), _value(value) {}
            ObjcAssociation() : _policy(0), _value(nil) {}
    
            uintptr_t policy() const { return _policy; }
            id value() const { return _value; }
            
            bool hasValue() { return _value != nil; }
        };
    
    
    大概的存放结构图
    image.png

    接下来我们可以重新回到对 objc_setAssociatedObject 方法的分析了。

    在这里会将方法的执行分为两种情况:

    • new_value != nil 设置/更新关联对象的值
    • new_value == nil 删除一个关联对象

    new_value != nil

    先来分析在 new_value != nil 的情况下,该方法的执行是什么样的:

    void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
        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);
    
            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;
                    j->second = ObjcAssociation(policy, new_value);
                } else {
                    (*refs)[key] = ObjcAssociation(policy, new_value);
                }
            } else {
                ObjectAssociationMap *refs = new ObjectAssociationMap;
                associations[disguised_object] = refs;
                (*refs)[key] = ObjcAssociation(policy, new_value);
                object->setHasAssociatedObjects();
            }
        }
        if (old_association.hasValue()) ReleaseValue()(old_association);
    }
    

    1.使用 old_association(0, nil) 创建一个临时的 ObjcAssociation 对象(用于持有原有的关联对象,方便在方法调用的最后释放值)
    2.调用 acquireValue 对 new_value 进行 retain 或者 copy

    static id acquireValue(id value, uintptr_t policy) {
        switch (policy & 0xFF) {
        case OBJC_ASSOCIATION_SETTER_RETAIN:
            return ((id(*)(id, SEL))objc_msgSend)(value, SEL_retain);
        case OBJC_ASSOCIATION_SETTER_COPY:
            return ((id(*)(id, SEL))objc_msgSend)(value, SEL_copy);
        }
        return value;
    }
    

    3.初始化一个 AssociationsManager,并获取唯一的保存关联对象的哈希表 AssociationsHashMap

    AssociationsManager manager;
    AssociationsHashMap &associations(manager.associations());
    

    4.先使用 DISGUISE(object) 作为 key 寻找对应的 ObjectAssociationMap
    5.如果没有找到,初始化一个 ObjectAssociationMap,再实例化 ObjcAssociation 对象添加到 Map 中,并调用 setHasAssociatedObjects 方法,表明当前对象含有关联对象

    ObjectAssociationMap *refs = new ObjectAssociationMap;
    associations[disguised_object] = refs;
    (*refs)[key] = ObjcAssociation(policy, new_value);
    object->setHasAssociatedObjects();
    

    6.如果找到了对应的 ObjectAssociationMap,就要看 key 是否存在了,由此来决定是更新原有的关联对象,还是增加一个

    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);
    }
    

    7.最后的最后,如果原来的关联对象有值的话,会调用 ReleaseValue() 释放关联对象的值

    struct ReleaseValue {
        void operator() (ObjcAssociation &association) {
            releaseValue(association.value(), association.policy());
        }
    };
    
    static void releaseValue(id value, uintptr_t policy) {
        if (policy & OBJC_ASSOCIATION_SETTER_RETAIN) {
            ((id(*)(id, SEL))objc_msgSend)(value, SEL_release);
        }
    }
    

    new_value == nil

    如果 new_value == nil,就说明我们要删除对应 key 的关联对象,实现如下:

    void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
        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);
    
            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);
    }
    

    这种情况下方法的实现与前面的唯一区别就是,我们会调用 erase 方法,擦除 ObjectAssociationMapkey 对应的节点。

    setHasAssociatedObjects()
    其实上面的两种情况已经将 objc_setAssociatedObject 方法的实现分析得很透彻了,不过,这里还有一个小问题来等待我们解决,setHasAssociatedObjects() 方法的作用是什么?

    inline void objc_object::setHasAssociatedObjects() {
        if (isTaggedPointer()) return;
    
     retry:
        isa_t oldisa = LoadExclusive(&isa.bits);
        isa_t newisa = oldisa;
        if (!newisa.indexed) return;
        if (newisa.has_assoc) return;
        newisa.has_assoc = true;
        if (!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)) goto retry;
    }
    

    它会将 isa 结构体中的标记位 has_assoc 标记为 true,也就是表示当前对象有关联对象,在这里我还想祭出这张图来介绍 isa 中的各个标记位都是干什么的。

    image.png

    关于实现
    关联对象又是如何实现并且管理的呢:

    • 关联对象其实就是 ObjcAssociation 对象
    • 关联对象由 AssociationsManager 管理并在 AssociationsHashMap 存储
    • 对象的指针以及其对应 ObjectAssociationMap 以键值对的形式存储在 AssociationsHashMap 中
      ObjectAssociationMap 则是用于存储关联对象的数据结构
    • 每一个对象都有一个标记位 has_assoc 指示对象是否含有关联对象

    参考:

    https://github.com/draveness/analyze/blob/master/contents/objc/%E5%85%B3%E8%81%94%E5%AF%B9%E8%B1%A1%20AssociatedObject%20%E5%AE%8C%E5%85%A8%E8%A7%A3%E6%9E%90.md

    相关文章

      网友评论

          本文标题:关联函数的实现原理

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