相关对象(AssociateObject)实现学习

2019-08-05  本文已影响0人  我才是臭吉吉

源代码copy

不废话,直接附上实现的源代码:

相关API声明在objc-runtime.mm中

1. 设置相关对象的值
/** 为类的实例对象设置相关对象 */
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) {
    _object_set_associative_reference(object, (void *)key, value, policy);
}


// 实现
void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
    // retain the new value (if any) outside the lock.
    // 初始化ObjcAssociation结构实例
    ObjcAssociation old_association(0, nil);
    // 对传入的新值value根据policy进行内存处理,并返回对象地址
    id new_value = value ? acquireValue(value, policy) : nil;
    {
        AssociationsManager manager;
        // 获取全局相关对象hashmap的起始地址
        AssociationsHashMap &associations(manager.associations());
        // object地址转换为查询地址
        disguised_ptr_t disguised_object = DISGUISE(object);
        if (new_value) {
            // break any existing association.
            // 查询全局哈希表中是否存在object的相关对象信息的容器
            AssociationsHashMap::iterator i = associations.find(disguised_object);
            if (i != associations.end()) {
                // secondary table exists
                // 存在object的相关对象信息容器(也是一张子哈希表)
                
                // 获取字表的起始地址
                ObjectAssociationMap *refs = i->second;
                
                // 查找是否存在key(即新加的属性)的相关信息
                ObjectAssociationMap::iterator j = refs->find(key);
                if (j != refs->end()) {
                    // 存在相同的相关信息(以前存储过了),直接修改
                    
                    // 将以前的值缓存给old_assosiation
                    old_association = j->second;
                    
                    // 在原地址中存入新值(ObjcAssociation结构体实例,成员为policy和new_value)
                    j->second = ObjcAssociation(policy, new_value);
                } else {
                    // 在该object中没有此属性key的相关信息保存,创建新的,直接存储
                    
                    // 在该地址下,存入键值对:键名为属性名key,值为ObjcAssociation结构体实例
                    (*refs)[key] = ObjcAssociation(policy, new_value);
                }
            } else {
                // create the new association (first time).
                // 全局哈希表中没有存储object的相关对象容器,创建新的容器(ObjectAssociationMap对象,也是个哈希表)
                ObjectAssociationMap *refs = new ObjectAssociationMap;
                
                // 将新创建的容器指针存储到全局哈希表中,键名为object的地址
                associations[disguised_object] = refs;
                
                // 在容器中存入相关对象值(ObjcAssociation结构体实例),键名为属性名key
                (*refs)[key] = ObjcAssociation(policy, new_value);
                
                // 标记object为存在相关对象信息(修改isa联合体实例中的相关位的值)
                object->setHasAssociatedObjects();
            }
        } else {
            // setting the association to nil breaks the association.
            // 传入的相关值是nil,则清除该相关对象的信息(清除属性的值)
            
            // 使用object地址再全局哈希表中查找,试图找到object的相关对象容器
            AssociationsHashMap::iterator i = associations.find(disguised_object);
            if (i !=  associations.end()) {
                // 已找到此容器(子哈希表,ObjectAssociationMap对象)
                ObjectAssociationMap *refs = i->second;
                
                // 查找此属性名key存储的信息
                ObjectAssociationMap::iterator j = refs->find(key);
                if (j != refs->end()) {
                    
                    // 查找到相关对象信息,缓存给old_association
                    old_association = j->second;
                    
                    // 清除此ObjectAssociationMap对象(相关对象实例)
                    refs->erase(j);
                }
            }
        }
    }
    // release the old value (outside of the lock).
    // 缓存的old_association中存在值,释放内存,清除缓存
    if (old_association.hasValue()) ReleaseValue()(old_association);
}

从代码中可以看出,所有的相关对象信息实际上是保存在全局的一个哈希表中。其中,以实例对象的地址( DISGUISE(object) )为键名,对应的值为另一张哈希表。此子哈希表中,以设置的属性名为键名,对应的值为相关值value和内存管理方式policy组成的结构体实例

而保存相关对象的值的操作中,系统:

  1. 首先查看是否已经存储过此object对应的子哈希表,不存在则创建;
  2. 查看子哈希表中,是否已经存储过此属性名key对应的结构体信息,存在即修改为新值,不存在则创建新的保存;
  3. 对与传入的value为nil时,清除子哈希表中属性名key对应的结构体信息,以达到清除相关值的目的(系统没有提供单独清空相关值属性的API )。
  4. 替换下来的相关值信息,抹掉并释放内存。
2. 获取相关对象的值
/** 为类的实例对象获取相关对象的值 */
id objc_getAssociatedObject(id object, const void *key) {
    return _object_get_associative_reference(object, (void *)key);
}


// 实现
id _object_get_associative_reference(id object, void *key) {
    id value = nil;
    // 声明内存管理方式指针变量(默认为assign)
    uintptr_t policy = OBJC_ASSOCIATION_ASSIGN;
    
    {
        AssociationsManager manager;
        // 获取全局相关对象哈希表(起始地址)【由于associations得到的是指向AssociationsHashMap的指针,故使用取地址符返回AssociationsHashMap的起始地址】
        AssociationsHashMap &associations(manager.associations());
    
        // 将object地址转换为查询地址(保存地址)
        disguised_ptr_t disguised_object = DISGUISE(object);
    
        // 视图查找到object对应的相关对象信息容器(AssociationsHashMap对象)
        AssociationsHashMap::iterator i = associations.find(disguised_object);
        if (i != associations.end()) {
            // 已找到,获取入口地址(ObjectAssociationMap对象地址)
            ObjectAssociationMap *refs = i->second;
            
            // 查找ObjectAssociationMap中,是否存在属性名key对应的相关信息实例
            ObjectAssociationMap::iterator j = refs->find(key);
            if (j != refs->end()) {
                // 已找到,记录入口地址(ObjcAssociation结构体实例的指针)
                ObjcAssociation &entry = j->second;
                // 取出对应的值和内存管理方式
                value = entry.value();
                policy = entry.policy();
                if (policy & OBJC_ASSOCIATION_GETTER_RETAIN) {
                    // 通过按位与运算,对需要retain的值,进行retain操作
                    objc_retain(value);
                }
            }
        }
    }
    if (value && (policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE)) {
        // 存在相关值,且内存管理方式为autorelease,进行autorelease操作
        objc_autorelease(value);
    }
    
    // 返回相关值
    return value;
}
3. 为实例对象清除所有的相关对象
/** 清除类的实例对象的所有相关对象 */
void objc_removeAssociatedObjects(id object) 
{
    // 查看对象isa联合体中has_assoc位的值来确定是否存在相关对象
    if (object && object->hasAssociatedObjects()) {
        _object_remove_assocations(object);
    }
}


// 实现
void _object_remove_assocations(id object) {
    vector< ObjcAssociation,ObjcAllocator<ObjcAssociation> > elements;
    
    {
        // 获取全局相关对象的整体哈希表
        AssociationsManager manager;
        AssociationsHashMap &associations(manager.associations());
    
        // 本身没有存储任何信息,直接返回
        if (associations.size() == 0) return;
    
        // 将object指针转换为查询的指针
        disguised_ptr_t disguised_object = DISGUISE(object);
        // 试图查询是否存在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;
            
            // 依次遍历子哈希表(ObjectAssociationMap对象的键值对信息)
            for (ObjectAssociationMap::iterator j = refs->begin(), end = refs->end(); j != end; ++j) {
                // 头插法插入到向量对象中
                elements.push_back(j->second);
            }
            // remove the secondary table.
            // 删除入口地址信息
            delete refs;
            // 清除子表(object容器中存储的所有信息,即ObjectAssociationMap对象)
            associations.erase(i);
            // 清除之后,object便没有任何相关对象保存
        }
    }
    // the calls to releaseValue() happen outside of the lock.
    // 对向量中存储的所有相关对象信息,依次释放内存
    for_each(elements.begin(), elements.end(), ReleaseValue());
}

相关问题

1. 在Category中添加的property属性,系统为什么不会自动合成setter和getter?
  1. 在编译期,类的内存布局已经确定,在Class的结构中,并没有专门存储Category相关信息的位置。
  2. Category是在APP启动时,在运行期才进行加载的,且是在对应的类加载完毕后才进行。最重要的,加载Category时,系统只是将内部声明的方法附加到Class的对应方法列表中,并不会对类的ivar列表进行操作。
  3. 最重要的,objc_category_t结构体中,根本没有变量成员,故自身也不能存储任何变量的值。
2. 相关对象是针对类的还是实例对象的?

显而易见,由API的实现,可以看到,所有的相关对象是与object参数,即类的实例对象进行绑定关联,并存储到全局哈希表中的。故设置的相关对象只对当前的调用者对象有效

上一篇 下一篇

猜你喜欢

热点阅读