zoukankan      html  css  js  c++  java
  • 浅谈 Objective-C Associated Objects

    简介

    Associated ObjectsObjective-C 2.0Runtime 的特性之一。 在 <objc/runtime.h> 中定义的三个方法,

    void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);
    id objc_getAssociatedObject(id object, const void *key);
    void objc_removeAssociatedObjects(id object);
    复制代码

    从上面可以看出, objc_setAssociatedObject 用于给对象添加关联对象,传入 nil 则可以移除已有的关联对象; objc_getAssociatedObject 用于获取关联对象; objc_removeAssociatedObjects 用于移除一个对象的所有关联对象。 object:传入关联对象的所属对象,也就是增加成员的实例对象,一般来说传入 selfkey:唯一标记,即可以使用 static char 作为 key 值,也可以使用 static void *kAssociatedObjectKey 指针作为 key 值,当然推荐使用 用 selector,使用 getter 方法的名称作为 key 值,可以利用 _cmd 来方便的取出 selectorvalue:传入关联对象。 policyobjc_AssociationPolicy 是一个 Objective-C 枚举类型,也代表关联策略。

    注意:objc_removeAssociatedObjects这个方法会移除一个对象的所有关联对象,一般通过给 objc_setAssociatedObject 函数传入 nil 来移除某个已有的关联对象。

    关联策略

    OBJC_ASSOCIATION_ASSIGN:弱引用关联对象,一般修饰词为 assignunsafe_unretainedOBJC_ASSOCIATION_RETAIN_NONATOMIC:强引用关联对象,非原子操作,修饰词为 strongnonatomicOBJC_ASSOCIATION_COPY_NONATOMIC:复制关联对象,非原子操作,修饰词为 copynonatomicOBJC_ASSOCIATION_RETAIN:强引用关联对象,原子操作,修饰词为 strongatomicOBJC_ASSOCIATION_COPY:复制关联对象,原子操作,修饰词为 copyatomic

    注意:OBJC_ASSOCIATION_ASSIGN 弱引用关联对象,一般修饰词为 assignunsafe_unretainedweak 有区别,当对象销毁时,指针的地址还是存在的,也就是说指针并没有被置为 nil,再次访问会造成野指针。

    实现原理

    objc_setAssociatedObject

    下面我们看下 Runtime 的源码。 以下源码来自于[opensource.apple.com]

    void objc_setAssociatedObject(id object, const void *key, id value, 
                             objc_AssociationPolicy policy)  {
        objc_setAssociatedObject_non_gc(object, key, value, policy);
    }
    复制代码

    通过调用关系,Associated Objects 核心实现在 _object_set_associative_reference 方法里面。

    _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对象
        ObjcAssociation old_association(0, nil);
        // 通过policy为value创建对应属性,如果policy不存在,则默认为assign
        id new_value = value ? acquireValue(value, policy) : nil;
        {
        	// 创建AssociationsManager对象
            AssociationsManager manager;
            // 在manager取_map成员,其实是一个map类型的映射
            AssociationsHashMap &associations(manager.associations());
            // 创建指针指向即将拥有成员的Class
    		// 至此该类已经包含这个关联对象
            disguised_ptr_t disguised_object = DISGUISE(object);
             // 以下是记录强引用类型成员的过程
            if (new_value) {
                // break any existing association.
                // 在即将拥有成员的Class中查找是否已经存在改关联属性
                AssociationsHashMap::iterator i = associations.find(disguised_object);
                if (i != associations.end()) {
                    // secondary table exists
                    // 当存在时候,访问这个空间的map
                    ObjectAssociationMap *refs = i->second;
                    // 遍历其成员对应的key
                    ObjectAssociationMap::iterator j = refs->find(key);
                    if (j != refs->end()) {
                    	// 如果存在key,重新更改Key的指向到新关联属性
                        old_association = j->second;
                        j->second = ObjcAssociation(policy, new_value);
                    } else {
                    	// 否则以新的key创建一个关联
                        (*refs)[key] = ObjcAssociation(policy, new_value);
                    }
                } else {
                    // create the new association (first time).
                    // key不存在的时候,直接创建关联
                    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.
                // 这种情况是policy不存在或者为assign的时候
                // 在即将拥有的Class中查找是否已经存在Class
                // 其实这里的意思就是如果之前有这个关联对象,并且是非assign形的,直接erase
                AssociationsHashMap::iterator i = associations.find(disguised_object);
                if (i != associations.end()) {
                	// 如果有该类型成员检查是否有key
                    ObjectAssociationMap *refs = i->second;
                    ObjectAssociationMap::iterator j = refs->find(key);
                    if (j != refs->end()) {
                    	// 如果有key,记录旧对象,释放
                        old_association = j->second;
                        refs->erase(j);
                    }
                }
            }
        }
        // release the old value (outside of the lock).
        // 如果存在旧对象,则将其释放
        if (old_association.hasValue()) ReleaseValue()(old_association);
    }
    复制代码

    源码中得出结论

    • Associated Objects 是一个 AssociationsManager 的结构体,维护了一个 spinlock_t 锁和一个 _map 的哈希表。
    • _map 哈希表中的键为 disguised_ptr_t
    inline disguised_ptr_t DISGUISE(id value) { return ~uintptr_t(value); }
    #ifndef _UINTPTR_T
    #define _UINTPTR_T
    typedef unsigned long		uintptr_t;
    #endif /* _UINTPTR_T */
    复制代码

    其实 DISGUISE 函数其实仅仅对 object 做了下位运算,得到一个指向 self 地址的指针,通过这个指针,可以找到对应的 value,即一个 AssociationsHashMap 哈希表。

    ObjectAssociationMap
    #if TARGET_OS_WIN32
        typedef hash_map<void *, ObjcAssociation> ObjectAssociationMap;
        typedef hash_map<disguised_ptr_t, ObjectAssociationMap *> AssociationsHashMap;
    #else
        typedef ObjcAllocator<std::pair<void * const, ObjcAssociation> > ObjectAssociationMapAllocator;
        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); }
        };
        typedef ObjcAllocator<std::pair<const disguised_ptr_t, ObjectAssociationMap*> > AssociationsHashMapAllocator;
        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); }
        };
    #endif
    复制代码

    AssociationsHashMapkeydisguised_ptr_tValue 则是ObjectAssociationMap。 在 ObjectAssociationMap 中以 keyself 指针,Value 则是 ObjcAssociation

    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; }
    };
    复制代码

    ObjcAssociation 存储着 _policy_value,而这两个值是调用 objc_setAssociatedObject 函数传入的值。

    总结:AssociationsHashMapkey-value 的形式保存从对象的 disguised_ptr_tObjectAssociationMap 的映射,而 ObjectAssociationMap 则保存了从 key 到关联对象 ObjcAssociation 的映射,这个数据结构保存了当前对象对应的所有关联对象,最后的 ObjcAssociation 存储了 policy 以及 value

    new_value
    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;
    }
    复制代码

    根据 acquireValue 函数,把传入的 value 通过对策略的判断返回新的 new_value

    new_value != nil 设置/更新关联对象的值

    // break any existing association.
    // 在即将拥有成员的Class中查找是否已经存在改关联属性
    AssociationsHashMap::iterator i = associations.find(disguised_object);
    if (i != associations.end()) {
        // secondary table exists
        // 当存在时候,访问这个空间的map
        ObjectAssociationMap *refs = i->second;
        // 遍历其成员对应的key
        ObjectAssociationMap::iterator j = refs->find(key);
        if (j != refs->end()) {
            // 如果存在key,重新更改Key的指向到新关联属性
            old_association = j->second;
            j->second = ObjcAssociation(policy, new_value);
        } else {
            // 否则以新的key创建一个关联
            (*refs)[key] = ObjcAssociation(policy, new_value);
        }
    } else {
        // create the new association (first time).
        // key不存在的时候,直接创建关联
        ObjectAssociationMap *refs = new ObjectAssociationMap;
        associations[disguised_object] = refs;
        (*refs)[key] = ObjcAssociation(policy, new_value);
        object->setHasAssociatedObjects();
    }
    复制代码
    • 获取唯一的保存关联对象的哈希表 AssociationsHashMap

    • 使用 DISGUISE(object) 作为 key 寻找对应的 ObjectAssociationMap

    • 如果没有找到,初始化一个 ObjectAssociationMap,再实例化 ObjcAssociation 对象添加到 Map 中,并调用 setHasAssociatedObjects 方法,表明当前对象已经含有关联对象。

    • 如果找到了对应的 ObjectAssociationMap,遍历其成员对应的 key 是否存在,如果存在 key,重新更改 Key 的指向到新关联属性,否则以新的 key 创建一个关联。

    如果 new_value == nil,就要删除对应 key 的关联对象。

    // setting the association to nil breaks the association.
    // 这种情况是policy不存在或者为assign的时候
    // 在即将拥有的Class中查找是否已经存在Class
    // 其实这里的意思就是如果之前有这个关联对象,并且是非assign形的,直接erase
    AssociationsHashMap::iterator i = associations.find(disguised_object);
    if (i != associations.end()) {
        // 如果有该类型成员检查是否有key
        ObjectAssociationMap *refs = i->second;
        ObjectAssociationMap::iterator j = refs->find(key);
        if (j != refs->end()) {
            // 如果有key,记录旧对象,释放
            old_association = j->second;
            refs->erase(j);
        }
    }
    复制代码

    policy 不存在或者为 assign 的时候,

    • 根据 DISGUISE(object) 作为 key 寻找对应的 ObjectAssociationMap
    • 如果找到了对应的 ObjectAssociationMap,遍历其成员对应的 key 是否存在,如果存在 key,调用 erase 方法,移除关联关系。
    // release the old value (outside of the lock).
    if (old_association.hasValue()) ReleaseValue()(old_association);
    复制代码

    如果存在旧对象,则将其释放。

    借助一张图objc_setAssociatedObject 原理

    objc_getAssociatedObject

    objc_getAssociatedObject 内部调用的是 _object_get_associative_reference

    id objc_getAssociatedObject(id object, const void *key) {
        return objc_getAssociatedObject_non_gc(object, 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) ((id(*)(id, SEL))objc_msgSend)(value, SEL_retain);
                }
            }
        }
        if (value && (policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE)) {
            ((id(*)(id, SEL))objc_msgSend)(value, SEL_autorelease);
        }
        return value;
    }
    复制代码
    • 获取唯一的保存关联对象的哈希表 AssociationsHashMap

    • 使用 DISGUISE(object) 作为 key 寻找对应的 ObjectAssociationMap

    • 如果找到了对应的 ObjectAssociationMap,遍历其成员对应的 key 是否存在,如果存在 key,获取 ObjcAssociation

    • 返回关联对象 ObjcAssociation 的值。

    objc_removeAssociatedObjects

    objc_removeAssociatedObjects 用来删除所有的关联对象,objc_removeAssociatedObjects 函数内部调用的是 _object_remove_assocations 函数。

    _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_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());
    }
    复制代码
    • 获取唯一的保存关联对象的哈希表 AssociationsHashMap

    • 如果哈希表 AssociationsHashMapsize0,直接 return

    • 使用 DISGUISE(object) 作为 key 寻找对应的 ObjectAssociationMap

    • 如果找到了对应的 ObjectAssociationMap,遍历其成员对应的 key 是否存在,如果存在 key,然后将所有的关联结构保存到 vector 中。

    • 删除关联关系,释放关联对象。

    参考链接

    www.jianshu.com/p/79479a09a… blog.leichunfeng.com/blog/2015/0…

  • 相关阅读:
    2016"百度之星"
    codeforces 55 div2 C.Title 模拟
    codeforces 98 div2 C.History 水题
    codeforces 97 div2 C.Replacement 水题
    codeforces 200 div2 C. Rational Resistance 思路题
    bzoj 2226 LCMSum 欧拉函数
    hdu 1163 九余数定理
    51nod 1225 余数的和 数学
    bzoj 2818 gcd 线性欧拉函数
    Codeforces Round #332 (Div. 2)D. Spongebob and Squares 数学
  • 原文地址:https://www.cnblogs.com/twodog/p/12135978.html
Copyright © 2011-2022 走看看