<acronym id='otihs'><em id='otihs'></em><td id='otihs'><div id='otihs'></div></td></acronym><address id='otihs'><big id='otihs'><big id='otihs'></big><legend id='otihs'></legend></big></address>
    <i id='otihs'><div id='otihs'><ins id='otihs'></ins></div></i>

    <ins id='otihs'></ins>
      <fieldset id='otihs'></fieldset>

    1. <span id='otihs'></span>
    2. <tr id='otihs'><strong id='otihs'></strong><small id='otihs'></small><button id='otihs'></button><li id='otihs'><noscript id='otihs'><big id='otihs'></big><dt id='otihs'></dt></noscript></li></tr><ol id='otihs'><table id='otihs'><blockquote id='otihs'><tbody id='otihs'></tbody></blockquote></table></ol><u id='otihs'></u><kbd id='otihs'><kbd id='otihs'></kbd></kbd>

      <code id='otihs'><strong id='otihs'></strong></code>

        <i id='otihs'></i>

          <dl id='otihs'></dl>

          iOS关联对象技术原理

          • 时间:
          • 浏览:8
          • 来源:124软件资讯网

            iOS 通过 runtime 的 API 可以给分类添加属性,关联属性总共有下边3个 API

            ///获取某个工具的关联属性
            id objc_getAssociatedObject(id object, const void *key) {
                return _object_get_associative_reference(object, (void *)key);
            }
            ///给某个工具添加关联属性
            void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) {
                _object_set_associative_reference(object, (void *)key, value, policy);
            }
            ///移除工具所有的关联属性
            void objc_removeAssociatedObjects(id object)

            通过 runtime 的源码可以看出关联属性并没有添加到 category_t(分类)里边,运行时也不汇合并到元类工具里边,而是存储在一个全局的AssociationsManager 里边,下边是这个 AssociationsManager 包罗的层级关系.

            image.png

            所有的关联属性 和 获取关联属性 移除关联属性都是通过一个 AssociationsManager来操作,类似于 OC 中 NSFileManager 的角色,通过通报进来的工具作为地址 取出这个工具所对应的关联列表,然后通过key 取出这个关联列表的关联属性 ObjcAssociation, ObjcAssociation 包罗了关联计谋 和 关联值.

            下边我会通过解读源码 来剖析AssociationsManager是怎样要关联的值和工具建设联系的.

            AssociationsManager 是一个 C++的类 用来举行对关联工具的属性添加 和 查找 移除等操作

            class AssociationsManager {
                static spinlock_t _lock;
                static AssociationsHashMap *_map;               // associative references:  object pointer -> PtrPtrHashMap. 这个_ map 里边存储的有关联列表
            public:
                AssociationsManager()   { _lock.lock(); }
                ~AssociationsManager()  { _lock.unlock(); }
                
                AssociationsHashMap &associations() { //可以看成是只初始化一次 类似与单例
                    if (_map == NULL)
                        _map = new AssociationsHashMap();
                    return *_map;
                }
            };

            关联列表是一个 hashMap 类似于 OC 的 NSDictionary ,其中用 disguised_ptr_t 作为 key , ObjectAssociationMap * 作为一个 value

            disguised_ptr_t 是 uintptr_t 的类型

            intptr_t 和uintptr_t 类型用来存放指针地址 。它们提供了一种可移植且宁静的要领声明指针  ,而且和系统中使用的指针长度相同  ,对于把指针转化成整数形式来说很有用 。

            可以把disguised_ptr_t明白为一个指针类型的变量

               class AssociationsHashMap : public unordered_map {
                public:
                    void *operator new(size_t n) { return ::malloc(n); }
                    void operator delete(void *ptr) { ::free(ptr); }
                };

            ObjectAssociationMap 也是一个 HashMap 存放的是 一个 void * key 就是关联属性时传进来的 key , ObjcAssociation 存放的关联属性计谋和值的信息

                class ObjectAssociationMap : public std::map {
                public:
                    void *operator new(size_t n) { return ::malloc(n); }
                    void operator delete(void *ptr) { ::free(ptr); }
                };

            ObjcAssociation 关联属性信息类 存放了关联计谋 和 通报进来关联的值 id 类型

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

            下边是对 objc_getAssociatedObject , objc_setAssociatedObject , objc_removeAssociatedObjects 详细实现的剖析

            objc_setAssociatedObject 添加关联属性的 API

            void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
                // retain the new value (if any) outside the lock.
               /// 旧的关联工具 由于关联属性时若是传 nil 可能会替换旧的关联属性 ,这就是移除某个关联属性时传 nil 的缘故原由
                ObjcAssociation old_association(0, nil);
                id new_value = value ? acquireValue(value, policy) : nil;
                {
                    AssociationsManager manager;
                   ///获取关联属性列表 ,取出来的列表是以工具为单元的 ,即某个工具的关联列表 ,这样就可以单独的关联某个工具的关联属性 而不与其他工具隔脱离
                    AssociationsHashMap &associations(manager.associations()); 
                  /// 将要添加关联属性的工具发生一个内存地址 做 key 存储 它的关联属性
                    disguised_ptr_t disguised_object = DISGUISE(object);
                  /// 若是要关联的值不为空 ,不为空时 就需要判断这个属性和 key 是不是第一天添加 ,即  void *key, id value 都是第一次通报进来 
                    if (new_value) {
                        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; //取出旧值 后边对这个旧值举行 release 操作
                               ///将新值存放到 key 对应的字典中去 
                                j->second = ObjcAssociation(policy, new_value);
                            } else { ///没有旧值直接将新值添加到字典里
                                (*refs)[key] = ObjcAssociation(policy, new_value);
                            }
                        } else { 
                              若是 key 工具的字典不存在 就建立一个字典 (hashMap 类似于字典的功效,本文为了利便明白将它称为字典)
                            ObjectAssociationMap *refs = new ObjectAssociationMap;
                            associations[disguised_object] = refs; 
                          ///将要关联属性和计谋封装到一个ObjcAssociation类里边 并凭据 key 添加到这个字典里
                            (*refs)[key] = ObjcAssociation(policy, new_value);
                            object->setHasAssociatedObjects();
                        }
                    } else {
                      ///若是添加关联的属性为空时 就需要取出之前关联的值 并把它擦除掉 相当于removeObjectForKey 
                    ///照旧凭据工具内存地址找到它的关联属性列表 ,然后通过 key 找到它关联属性的实体(ObjcAssociation这个类) 最后擦除掉 相当于 free 从内存中移除
                        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);
            }

            objc_getAssociatedObject 关联工具取值的操作

            id _object_get_associative_reference(id object, void *key) {
                id value = nil;
                uintptr_t policy = OBJC_ASSOCIATION_ASSIGN; 
                {
                  ///照旧通过 AssociationsManager 找到所有关联工具种别 ,然后通过传入 object 找到某个工具的关联列表 ,然后通过 key 找到这个工具关联属性列表的某个实体(ObjcAssociation) 最后凭据关联计谋返回这个属性的值 
                    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();
                            ///取出关联值和计谋 发送新闻 类似与 [obj retain]
                            if (policy & OBJC_ASSOCIATION_GETTER_RETAIN) ((id(*)(id, SEL))objc_msgSend)(value, SEL_retain);
                        }
                    }
                }
               /// 若是这个工具是延时释放的类型 类似与 OC Array String 这些不是 alloc 来的工具 都要执行 [obj autorelease]来释放 
                if (value && (policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE)) {
                    ((id(*)(id, SEL))objc_msgSend)(value, SEL_autorelease);
                }
                return value;
            }

            objc_removeAssociatedObjects 移除该工具所有的关联属性列表

            void _object_remove_assocations(id object) {
                vector< ObjcAssociation,ObjcAllocator > 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);
                   ///若是这个工具有关联的属性列表 那么久便利它关联的属性列表 然后通过便利将这些关联内容 一个个从字典里边擦除  先擦除工具列表关联的属性列表 然后将这个工具关联属性的 hashMap 擦除掉 相当于 [dict removeAllObjects] 然后再从全局 AssociationsManager 移除 这个工具关联的字典 , 又相当于 从一个全局大字典里 把 dict这个工具的小字典 给移除了 
                    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());
            }

            以上代码看起来并不难 除了一些 C++ 语法难以明白外 也并不需要完全知道每行代码怎么实现 ,或许思绪就是 通过全局大字典 ,找到某个工具相关的小字典 ,然后这个小字典里存放了 一个key 对应一个属性值,最后取出这个治理属性的计谋和值

            写到最后 AssociationsManager 这个类不是一个单例类

            class AssociationsManager {

            static spinlock_t _lock;

            static AssociationsHashMap *_map; // associative references: object pointer -> PtrPtrHashMap.

            public:

            AssociationsManager() { _lock.lock(); } //默认无参结构函数 工具建立时自动挪用 执行加锁 这样多个线程会见 _map 时不会泛起问题

            ~AssociationsManager() { _lock.unlock(); } //析构函数 工具是否时间举行解锁的操作

            ///可以看做单例工具 在 AssociationsManager 建立时间 加锁 当 AssociationsManager 释放时间 解锁 ,防止多线程会见时间 对统一个 _map 多次建立 是一种懒汉模式单例

            AssociationsHashMap &associations() {

            if (_map == NULL)

            _map = new AssociationsHashMap();

            return *_map;

            }

            };

            它里边有个 spinlock_t锁 对 _map 这个全局唯一的实例 举行加锁息争锁 ,由于懒汉模式的单例 需要在多个线程会见 _map 时间举行加锁掩护