iOS深入原理

OC属性关联的实现原理

2020-10-27  本文已影响0人  Sweet丶

OC中在分类中添加属性用属性关联技术来实现存取值的:

- (void)setWeight:(int)weight{
    objc_setAssociatedObject(self, @selector(weight), @(weight), OBJC_ASSOCIATION_ASSIGN);
}

- (int)weight{
    return [objc_getAssociatedObject(self, _cmd) intValue];
}

- (void)removeWeight{
    objc_removeAssociatedObjects(self);
}

那么系统的属性关联功能是如何实现的呢?

  1. 属性关联怎么存储对象对应的key和value的?又是怎么取值的?
  2. 项目中可能会有很多的对象都使用了属性关联,通过什么来管理所有对象的属性关联?

通过objc的相关源码追踪可以查看到:

  1. 系统通过一个AssociationsManager的全局对象来管理所有对象的属性关联,AssociationsManager内部有一个哈希表AssociationsHashMap, 以对象disguised_object为key,value为ObjectAssociationMap.
  2. ObjectAssociationMap哈希表内部存放了该对象所有的关联属性,key为设置属性关联时的key,value为ObjcAssociation对象。
  3. ObjcAssociation对象中有设置属性关联时的policy和value。

注意点:

  1. 如果设置的value为nil,那么会使得ObjectAssociationMap哈希表中对应的那个键值对会删除。
  2. 如果使用objc_removeAssociatedObjects会将AssociationsManager对象的AssociationsHashMap中该对象的键值对移除。
  3. 当对象引用计数为0是会进入dealloc,追踪源码可查看到,销毁对象的过程中会销毁对象在AssociationsHashMap中键值对. 具体移除看_object_remove_assocations函数。
void *objc_destructInstance(id obj) 
{
    if (obj) {
        // Read all of the flags at once for performance.
        bool cxx = obj->hasCxxDtor();
        bool assoc = obj->hasAssociatedObjects();

        // This order is important.
        if (cxx) object_cxxDestruct(obj);
        if (assoc) _object_remove_assocations(obj);
        obj->clearDeallocating();
    }

    return obj;
}

附:设置属性关联的源码

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);
}
上一篇 下一篇

猜你喜欢

热点阅读