关联对象的实现

关联对象的实现
介绍

我们在开发中都使用过category,并且都了解category不能添加属性,因为在runtime时候,类的成员变量已经固定,并且categery的底层实现中没有设置存放成员变量的逻辑,所以无法给category添加属性,如果要添加属性需要使用关联对象来处理

  • 添加
objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
  • 获取
objc_getAssociatedObject(id object, const void *key);
  • 移除
objc_removeAssociatedObjects(id object) 
objc_setAssociatedObject的实现
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) {
    _object_set_associative_reference(object, (void *)key, value, policy);
}

下面我们仔细看一下_object_set_associative_reference(object, (void *)key, value, policy)

void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
    if (!object && !value) return;
    
    assert(object);
    
    if (object->getIsa()->forbidsAssociatedObjects())
        _objc_fatal("objc_setAssociatedObject called on instance (%p) of class %s which does not allow associated objects", object, object_getClassName(object));
    
    // 创建一个 ObjcAssociation对象,初始化policy为OBJC_ASSOCIATION_ASSIGN,value为nil。
    ObjcAssociation old_association(0, nil);//(1)
    //如果value有值,判断策略相应处理赋值给new_value
    id new_value = value ? acquireValue(value, policy) : nil;//(2)
    {   //c++默认初始化对象
        AssociationsManager manager;//(3)
        //获取全局的AssociateiionsMap
        AssociationsHashMap &associations(manager.associations());//(4)
        //将对象转化为disguised_ptr_t类型的对象
        disguised_ptr_t disguised_object = DISGUISE(object);//(5)
        if (new_value) {
            //通过AssociationsHashMap的迭代器,查找object对应的ObjectAssociationMap
            AssociationsHashMap::iterator i = associations.find(disguised_object);(6)
            if (i != associations.end()) {
                // secondary table exists,找到object对应的关联对象表
                ObjectAssociationMap *refs = i->second;
                //再使用迭代器,查找外部参数key所对应的关联属性
                ObjectAssociationMap::iterator j = refs->find(key);
                if (j != refs->end()) {
                		//保存旧关联,用于最后的释放
                    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).
                //object第一次添加关联对象,则为object创建关联表ObjectAssociationMap
                ObjectAssociationMap *refs = new ObjectAssociationMap
                //将ObjectAssociationMap添加进AssociationsHashMap中
                associations[disguised_object] = refs;
                //添加关联属性
                (*refs)[key] = ObjcAssociation(policy, new_value);
                //设置object的isa中的has_assoc值为true,表示有关联对象,用于后期对象的释放等操作
                object->setHasAssociatedObjects();
            }
        } else {
            // setting the association to nil breaks the association.
            //value为nil,释放object中与key有关的关联属性
            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)ObjcAssociation:关联对象的结构,存储着关联策略_policy和关联对象的值_value

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

(2)acquireValue

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

判断引用策略,做相应处理,只处理了retain和copy两种

  • 如果为Retian的,调用objc_retain将value的引用计数加一
  • 如果是Copy,调用Value的copy方法生成新的拷贝。

(3)AssociationsManager

spinlock_t AssociationsManagerLock;

class AssociationsManager {
    // associative references: object pointer -> PtrPtrHashMap.
    static AssociationsHashMap *_map;
public:
		//初始化AssociationsManager的时候,调用lock.lock()方法
    AssociationsManager()   { AssociationsManagerLock.lock(); }
    //析构的时候调用lock.unlock()
    ~AssociationsManager()  { AssociationsManagerLock.unlock(); }
    
    //associations()方法获取全局的AssociationsHashMap单例
    AssociationsHashMap &associations() {
        if (_map == NULL)
            _map = new AssociationsHashMap();
        return *_map;
    }
};

AssociationsHashMap *AssociationsManager::_map = NULL;

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

(4)AssociationsHashMap

ObjectAssociationMap 保存了从 key 到关联对象 ObjcAssociation 的映射,这个数据结构保存了当前对象对应的所有关联对象,是一张保存了与对象有关的所有的关联属性的表。key是代码中定义的key,value是ObjcAssociation

AssociationsHashMap 用与保存从对象的 disguised_ptr_tObjectAssociationMap 的映射。其中key是 disguised_ptr_t ,value是 ObjectAssociationMap

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

上面的代码中包含其他结构,我们来看一下

【1】ObjectAssociationMap:Key是void * const(内存地址),Value是ObjcAssociation

它继承自std::map<void *, ObjcAssociation, ObjectPointerLess, ObjectAssociationMapAllocator>泛型结构,泛型定义如下

std::map的泛型定义
	template <class _Key, class _Tp,class _Compare = less<_Key>, class _Allocator = allocator<pair<const _Key, _Tp> > >

我们分析一下std::map<void *, ObjcAssociation, ObjectPointerLess, ObjectAssociationMapAllocator>结构

  • _Key:void *,即无类型指针,表示内存地址。
  • _Tp:ObjcAssociation,用来存放关联对象的值与关联策略。
  • _Compare:ObjectPointerLess,提供比较算法(比对指针地址)。
  • _Allocator:ObjectAssociationMapAllocator,它的类型为ObjcAllocator<std::pair<void * const, ObjcAssociation> >,用std::pair实现的键值对,Key是void * const(内存地址)是设置关联对象的key的内存地址,Value是ObjcAssociation

std::pair主要的作用是将两个数据组合成一个数据,两个数据可以是同一类型或者不同类型。例如std::pair<int,float> 或者 std::pair<double,double>等。初始化一个pair可以使用构造函数,也可以使用std::make_pair函数

【2】AssociationsHashMap:Key是disguised_ptr_t,Value是ObjectAssociationMap *的键值对。

它继承自unordered_map <disguised_ptr_t, ObjectAssociationMap *, DisguisedPointerHash, DisguisedPointerEqual, AssociationsHashMapAllocator>,解释下这个泛型的意思

  • _Key(key_type):disguised_ptr_t,disguised_ptr_t是unsigned long类型,用来存放指针的。

  • _Tp(mapped_type):ObjectAssociationMap *,代表Map中存储的值类型。

  • _Hash(hasher):DisguisedPointerHash,提供hash算法。

  • _Pred(key_equal):DisguisedPointerEqual,提供equal算法(两个指针相同则相等)。

  • _Alloc(allocator_type):构造函数的类型,这里构造函数类型是ObjcAllocator<std::pair<const disguised_ptr_t, ObjectAssociationMap *> >,也是一个泛型,用std::pair实现Key是disguised_ptr_t,Value是ObjectAssociationMap *的键值对。

(5)disguised_ptr_t 与DISGUISE函数

    typedef unsigned long		uintptr_t;

    typedef uintptr_t disguised_ptr_t;
    inline disguised_ptr_t DISGUISE(id value) { return ~uintptr_t(value); }
    inline id UNDISGUISE(disguised_ptr_t dptr) { return id(~dptr); }
复制代码
  • disguised_ptr_t实际上是一个unsigned long,它的长度与指针相同,所以被当做指针使用。

  • DISGUISE(id value)用来将对象转化为disguised_ptr_t类型的对象 此函数返回~uintptr_t(value),value是id,也就是是对象指针,将它包装成unsigned long类型,载逐位取反后返回。

  • UNDISGUISE(disguised_ptr_t dptr)执行DISGUISE的反操作返回 id(~dptr),将disguised_ptr_t逐位取反后,返回对象(id)指针。

实践并总结

如果我们给一个分类的对象obj添加一个Hello属性,初始值是Hello,代码如下例如

objc_setAssociatedObject(obj, @selector(hello), @"Hello", OBJC_ASSOCIATION_RETAIN_NONATOMIC);

对应的关联对象 为ObjcAssociation(OBJC_ASSOCIATION_RETAIN_NONATOMIC, @"Hello")它在内存中的存储如下

在这里插入图片描述

objc_getAssociatedObject的实现
id objc_getAssociatedObject(id object, const void *key) {
  return _object_get_associative_reference(object, (void *)key);
}

下面我们看看_object_get_associative_reference()的实现

id _object_get_associative_reference(id object, void *key) {
		//创建初始值为nil的value,默认引用策略为assign
    id value = nil;
    uintptr_t policy = OBJC_ASSOCIATION_ASSIGN;
    {
    		//创建manager,加锁
        AssociationsManager manager;
        //获取全局的AssociateiionsMap
        AssociationsHashMap &associations(manager.associations());
        //将对象转化为disguised_ptr_t类型的disguised_object对象
        disguised_ptr_t disguised_object = DISGUISE(object);
        //在全局的AssociationsMap中根据disguised_object查找对应的ObjectAssociationMap
        AssociationsHashMap::iterator i = associations.find(disguised_object);
        if (i != associations.end()) {
        		//获取objectAssociationMap
            ObjectAssociationMap *refs = i->second;
            //在objectAssociationMap中根据key查找ObjectAssociation
            ObjectAssociationMap::iterator j = refs->find(key);
            if (j != refs->end()) {
                ObjcAssociation &entry = j->second;
                //从ObjectAssociation中获取value和policy
                value = entry.value();
                policy = entry.policy();
                // 如果引用策略是Retain模式
                if (policy & OBJC_ASSOCIATION_GETTER_RETAIN) {
                   // 调用value的retian方法,引用计数加一。
                    objc_retain(value);
                }
            }
        }
    }
    // 如果value存在,并且引用策略是AutoRelease模式。
    if (value && (policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE)) {
        // 调用value的autorelease方法,将value设置为autorelease。
        objc_autorelease(value);
    }
    return value;
}
objc_removeAssociatedObjects的实现
void objc_removeAssociatedObjects(id object) 
{
  if (object && object->hasAssociatedObjects()) {
     _object_remove_assocations(object);
  }
}

下面我们看看_object_remove_assocations()的实现

void _object_remove_assocations(id object) {
    vector< ObjcAssociation,ObjcAllocator<ObjcAssociation> > elements;
    {
    		//获取manager,内部加锁
        AssociationsManager manager;
        //获取全局的AssociationHashMap
        AssociationsHashMap &associations(manager.associations());
        //如果没有全局的AssociationHashMap,并表示没有关联属性,流程结束
        if (associations.size() == 0) return;
        
        //在AssociationsHashMap中查找disguised_object对应的ObjectAssociationMap。
        disguised_ptr_t disguised_object = DISGUISE(object);
        AssociationsHashMap::iterator i = associations.find(disguised_object);
        if (i != associations.end()) {
            //获取objectAssciationMap
            ObjectAssociationMap *refs = i->second;
            for (ObjectAssociationMap::iterator j = refs->begin(), end = refs->end(); j != end; ++j) {
           		//将所有的ObjectAssociation都存入vector中。
                elements.push_back(j->second);//(1)
            }
            //移除objectAssociationMap二级表
            delete refs;
            //释放该二级表在全局associationHashMap中的空间
            associations.erase(i);
        }
    }
    // 循环释放vector中保存的每一个ObjectAssociation 
    for_each(elements.begin(), elements.end(), ReleaseValue());
}

(1)push_back()

push_back()是箱vector的尾部添加元素,我们看一下什么是vector。

vector是和array类似的容器,不过相对于array,vector具有动态性,即在元素的插入和删除过程中,vector自身回去调整所占空间,不需认为干预。

  • begin():返回指向容器中第一个元素的迭代器。
  • end():返回指向容器最后一个元素所在位置后一个位置的迭代器,通常和 begin() 结合使用
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员的修养

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值