美文网首页
Objective-C--关联对象(AssociateObjec

Objective-C--关联对象(AssociateObjec

作者: 人生看淡不服就干 | 来源:发表于2017-07-24 17:54 被阅读49次

    关联对象的用途


    在Category中为已经注册的类增加存储字段,模拟实例变量。

    @implementation GPBChatGroupInfoForList (Extension)
    
    -(NSNumber*)isStick{
        return objc_getAssociatedObject(self, @selector(isStick));
    }
    
    -(void)setIsStick:(NSNumber*)isStick{
        objc_setAssociatedObject(self, @selector(isStick),isStick, OBJC_ASSOCIATION_RETAIN);
    }
    
    @end
    

    关联对象存储原理


    所有的关联对象都由一个全局的AssociationsManager管理,AssociationsManager里面是由一个静态AssociationsHashMap来存储所有的关联对象的。这相当于把所有对象的关联对象都存在一个全局map里面。而map的的key是这个对象的指针地址(任意两个不同对象的指针地址一定是不同的),而这个map的value又是另外一个AssociationsHashMap,里面保存了关联对象的kv对。

    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) 
    {
        if (object && object->hasAssociatedObjects()) {
            _object_remove_assocations(object);
        }
    }
    
    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;
    }
    
    void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
        // retain the new value (if any) outside the lock.
        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);
            if (new_value) {
                // break any existing association.
                AssociationsHashMap::iterator i = associations.find(disguised_object);
                if (i != associations.end()) {
                    // secondary table exists
                    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 {
                    // create the new association (first time).
                    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.
                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);
    }
    
    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());
    }
    

    关联对象如何实现weak存储


    runtime暴露的添加关联对象接口中不能指定weak存储类型

    typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
        OBJC_ASSOCIATION_ASSIGN = 0,   
        OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1,
        OBJC_ASSOCIATION_COPY_NONATOMIC = 3,   
        OBJC_ASSOCIATION_RETAIN = 01401,  
        OBJC_ASSOCIATION_COPY = 01403 
    };
    

    那我们如何自己实现weak效果呢?

    思路是这样的,虽然runtime没有开放weak解决方案,但OBJC对象是可以实现weak的,所以让需要被修饰的对象去持有一个strong的对象,然后当被修饰的对象被释放的时候,持有的对象也会被释放,那么我们就可以捕捉到释放的事件,进而使用OBJC_ASSOCIATION_ASSIGN 来实现弱引用,在释放事件里面再将其释放掉,进而实现weak功能。

    // 定义一个对象,使用block来回调析构函数。
    typedef void (^DeallocBlock)();
    
    @interface OriginalObject : NSObject
    @property (nonatomic, copy) DeallocBlock block;
    - (instancetype)initWithBlock:(DeallocBlock)block;
    @end
    
    @implementation OriginalObject
    
    - (instancetype)initWithBlock:(DeallocBlock)block{
        self = [super init];
        if (self) {
            self.block = block;
        }
        return self;
    }
    
    - (void)dealloc {
        self.block ? self.block() : nil;
    }
    
    @end
    
    @interface NSObject (property)
    @property (nonatomic, weak) id objc_weak_id;
    @end
    
    @implementation NSObject (property)
    
    - (id)objc_weak_id {
        return objc_getAssociatedObject(self, _cmd);
    }
    
    - (void)setObjc_weak_id:(id)objc_weak_id {
        OriginalObject *ob = [[OriginalObject alloc] initWithBlock:^{
            objc_setAssociatedObject(self, @selector(objc_weak_id), nil, OBJC_ASSOCIATION_ASSIGN);
        }];
        // 这里关联的key必须唯一,如果使用_cmd,对一个对象多次关联的时候,前面的对象关联会失效。
        objc_setAssociatedObject(objc_weak_id, (__bridge const void *)(ob.block), ob, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
        objc_setAssociatedObject(self, @selector(objc_weak_id), objc_weak_id, OBJC_ASSOCIATION_ASSIGN);
    }
    
    @end
    

    关联对象需要手动清理吗?

    不需要手动清理,runtime的销毁对象函数objc_destructInstance里面会判断这个对象有没有关联对象,如果有,会调用_object_remove_assocations做关联对象的清理工作。

    - (void)dealloc {
        _objc_rootDealloc(self);
    }
    
    void _objc_rootDealloc(id obj)
    {
        assert(obj);
        obj->rootDealloc();
    }
    
    inline void objc_object::rootDealloc()
    {
        if (isTaggedPointer()) return;
        object_dispose((id)this);
    }
    
    id  object_dispose(id obj)
    {
        if (!obj) return nil;
    
        objc_destructInstance(obj);    
        free(obj);
    
        return nil;
    }
    
    void *objc_destructInstance(id obj) 
    {
        if (obj) {
            Class isa = obj->getIsa();
    
            if (isa->hasCxxDtor()) {
                object_cxxDestruct(obj);
            }
    
            if (isa->instancesHaveAssociatedObjects()) {
                _object_remove_assocations(obj);
            }
    
            objc_clear_deallocating(obj);
        }
    
        return obj;
    }
    

    相关文章

      网友评论

          本文标题:Objective-C--关联对象(AssociateObjec

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