objective-c中关联引用的底层实现

我们知道关联引用(associative  references)可以为已存在的实例对象增加实例变量。该技术主要由以下三个函数实现相应的功能:

1.   void  objc_setAssociatedObject(id  object, void *key, id  value,objc_AssociationPolicy  policy)

这个方法用于为对象object添加以key指定的地址作为关键字,以value为值的关联引用,第四个参数policy指定关联引用的存储策略。

通过将value置为nil就可以删除对应key的关联。

2.   id  objc_getAssociatedObject(id  object, void *key)

返回对象object以key为关键字的关联对象。如果没有关联对象则返回nil。

3.   void  objc_removeAssociatedObjects(id  object)

断开object的所有关联。

至于以上函数的具体用法就不多说了,相信来看原理的客官们已经都会了( ^_^ )

下面是一些源码中会用到的结构体和函数定义,不想太在意细节的可以直接略过,也可以在后面遇到的时候回过头来看:

enum {
    OBJC_ASSOCIATION_SETTER_ASSIGN      = 0,
    OBJC_ASSOCIATION_SETTER_RETAIN      = 1,
    OBJC_ASSOCIATION_SETTER_COPY        = 3,            // NOTE:  both bits are set, so we can simply test 1 bit in releaseValue below.
    OBJC_ASSOCIATION_GETTER_READ        = (0 << 8),
    OBJC_ASSOCIATION_GETTER_RETAIN      = (1 << 8),
    OBJC_ASSOCIATION_GETTER_AUTORELEASE = (2 << 8)
};

enum {
    OBJC_ASSOCIATION_ASSIGN = 0,           /**< Specifies a weak reference to the associated object. */
    OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, /**< Specifies a strong reference to the associated object.
                                            *   The association is not made atomically. */
    OBJC_ASSOCIATION_COPY_NONATOMIC = 3,   /**< Specifies that the associated object is copied.
                                            *   The association is not made atomically. */
    OBJC_ASSOCIATION_RETAIN = 01401,       /**< Specifies a strong reference to the associated object.
                                            *   The association is made atomically. */
    OBJC_ASSOCIATION_COPY = 01403          /**< Specifies that the associated object is copied.
                                            *   The association is made atomically. */
};

inline disguised_ptr_t DISGUISE(id value) { return ~uintptr_t(value); }
inline id UNDISGUISE(disguised_ptr_t dptr) { return id(~dptr); }

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

spinlock_t AssociationsManager::_lock = SPINLOCK_INITIALIZER;
AssociationsHashMap *AssociationsManager::_map = NULL;

class ObjectAssociationMap : public std::map<void *, ObjcAssociation, ObjectPointerLess, ObjectAssociationMapAllocator>

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; }
};
if (value && (policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE)) {
        ((id(*)(id, SEL))objc_msgSend)(value, SEL_autorelease);
    }
    return 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;
}

static void releaseValue(id value, uintptr_t policy) {
    if (policy & OBJC_ASSOCIATION_SETTER_RETAIN) {
        ((id(*)(id, SEL))objc_msgSend)(value, SEL_release);
    }
}

struct ReleaseValue {
    void operator() (ObjcAssociation &association) {
        releaseValue(association.value(), association.policy());
    }
};

进入第一个函数(setAssociatedObject)的最终实现:(因为保存为代码的时候总是有莫名的字符添加上去,所以就直接贴出来了)

void_object_set_associative_reference(id object, void *key, id value, uintptr_tpolicy) {

    // retain the new value (if any) outside the lock.

    ObjcAssociation old_association(0, nil);    //构造了一个表示旧值的对象

    //根据相应的策略做retaincopy操作,见acquireValue函数

    id new_value = value ? acquireValue(value,policy) : nil;

    {

        AssociationsManager manager;

      //获得全局map对象的引用

        AssociationsHashMap&associations(manager.associations());

        disguised_ptr_t disguised_object =DISGUISE(object);

        //如果新值不为空

        if (new_value) {

            // break any existing association.

            //找到对应object的关联map容器

            AssociationsHashMap::iterator i =associations.find(disguised_object);

            if (i != associations.end()) {

                // secondary table exists

                //取得关联的map

                ObjectAssociationMap *refs =i->second;

                //取得关联的具体值

                ObjectAssociationMap::iteratorj = 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 {//object是第一次关联对象,新建一个map容器用于保存它的关联map

                // create the new association (first time).

                ObjectAssociationMap *refs =new ObjectAssociationMap;

                //将该值设置在全局map

                associations[disguised_object]= refs;

         //为具体的key赋值

                (*refs)[key] =ObjcAssociation(policy, new_value);

         //设置类中的标志

                Class cls =object->getIsa();

               cls->setInstancesHaveAssociatedObjects();

            }

        } else {//新值为空,可用于删除某个关联对象

            // setting the association to nil breaks the association.

            AssociationsHashMap::iterator i =associations.find(disguised_object);

      //如果有绑定到该对象的关联引用

            if (i !=  associations.end()) {

         //取得绑定的关联对象引用表

                ObjectAssociationMap *refs =i->second;

        //查找key对应的关联对象

                ObjectAssociationMap::iteratorj = refs->find(key);

                if (j != refs->end()) {//如果已经绑定了关联引用

                    //保存旧值,后面还要进一步处理

                    old_association =j->second;

                    //map中删掉该项

                    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;

//policy的初值赋为0

    uintptr_t policy = OBJC_ASSOCIATION_ASSIGN;

    {

        AssociationsManager manager;

        //获得全局map对象的引用

        AssociationsHashMap&associations(manager.associations());

        //objectDISGUISE操作

        disguised_ptr_t disguised_object =DISGUISE(object);

        //objectDISGUISE操作结果作为keyHashMap中查找相应的值

        AssociationsHashMap::iterator i =associations.find(disguised_object);

        //有对应的结果执行下列语句;没有则返回nil

        if (i != associations.end()) {

            //取得上次查找结果的second部分(该部分还是一个map

            ObjectAssociationMap *refs =i->second;

            //根据传入的key进一步在上次的结果集中查找

            ObjectAssociationMap::iterator j =refs->find(key);

            //如果有对应的值,进行下一步;否则返回nil

            if (j != refs->end()) {

                //取出关联对象的模型实体(包括了关联的策略和关联的具体对象)

                ObjcAssociation &entry =j->second;

                //取得关联的对象

                value = entry.value();

                //取得关联的策略

                policy = entry.policy();

                //根据相应的policy做一些后续操作

                if (policy & OBJC_ASSOCIATION_GETTER_RETAIN)((id(*)(id, SEL))objc_msgSend)(value, SEL_retain);

            }

        }

    }

    //有值的时候进行policy的检测和处理

    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<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.

            //找到它所关联的所有对象(一个map

            ObjectAssociationMap *refs =i->second;

      //将所有要删除的对象保存起来

            for (ObjectAssociationMap::iteratorj = refs->begin(), end = refs->end(); j != end; ++j) {

               elements.push_back(j->second);

            }

            // remove the secondary table.

            delete refs;

      //在全局关联引用map中,删除该对象对应的表

            associations.erase(i);

        }

    }

// the calls toreleaseValue() happen outside of the lock.

//删除指定对象所有的关联值,会根据设置的policy做一些后续的操作,详情见上面介绍的函数。

    for_each(elements.begin(), elements.end(),ReleaseValue());

}

 

通过上面的分析我们也可以知道,其实关联引用就是一个二级map的建立、查找、删除的操作。假设已建好了所有需要的表结构,其搜索过程如下图所示:


综上所述我们可以得出如下结论:关联引用的关联是以实例对象为基本单位进行的,即使是同一个类的实例对象,它们所关联的对象个数和值都可以不同;已关联的对象可以选择性删除或全部删除;关联的对象只要设置合适的策略就可以在撤销关联时不用手动置为nil,相关函数会帮我们完成这项操作(*^-^*)



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值