上一篇博客继续

第二种方法

Func2 利用关联实现对已存在的类添加成员变量的效果

涉及两个主要的API

给某一个对象关联一个对象
objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key, id _Nullable value, objc_AssociationPolicy policy)

获取某一个对象的关联对象
id _Nullable objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key)

举个例子

objc_setAssociatedObject(self.father, "testKey", @"我是testKey的value", OBJC_ASSOCIATION_RETAIN);
NSLog(@"关联对象testKey的value:%@",objc_getAssociatedObject(self.father, "testKey"));

打印结果:2020-03-06 10:54:25.145628+0800 XSTest[30259:1297570] 关联对象testKey的value:我是testKey的value

用起来比较简单,API的具体用法百度即可,我们主要看,关联是如何实现的,我们一步步追溯objc_setAssociatedObject后边的源码

Step1 objc-runtime.mm line 652

static void
_base_objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
{
  _object_set_associative_reference(object, key, value, policy);
}
void
_object_set_associative_reference(id object, const void *key, id value, uintptr_t policy)
{
    // 判空
    if (!object && !value) return;

	// 判断本类对象是否允许关联其他对象.如果允许则进入代码块
    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));

	// 将被关联的对象封装成DisguisedPtr方便在后边hash表中的管理,它的作用就像是一个指针
    DisguisedPtr<objc_object> disguised{(objc_object *)object};
    // 将需要关联的对象,封装成ObjcAssociation,方便管理
    ObjcAssociation association{policy, value};

    // 处理policy为retain和copy的修饰情况,
    association.acquireValue();

    {
    	// 获取关联对象管理者对象
        AssociationsManager manager;
        // 根据管理者对象获取对应关联表(HashMap)
        AssociationsHashMap &associations(manager.get());

        if (value) {
        	// 如果这个disguised存在于ObjectAssociationMap()中,则替换,如果不存在则初始化后在插入
        	// 这里说明一下,我们关联的对象关系存在于ObjectAssociationMap中,而
        	//	ObjectAssociationMap有多个,所以,这一步是对ObjectAssociationMap的一个管理,下边才是对我们要关联的对象的操作
            auto refs_result = associations.try_emplace(disguised, ObjectAssociationMap{});
            // 如果这是此对象第一次被关联
            if (refs_result.second) {
               // 修改isa_t中的has_assoc字段,标记其被关联状态
                object->setHasAssociatedObjects();
            }

            // 这里才是对我们要关联的对象操作
            auto &refs = refs_result.first->second;
            // 想map中插入key value对
            auto result = refs.try_emplace(key, std::move(association));
            // 这里没有看懂,为什么没有第二个就要交换一下..
            if (!result.second) {
                association.swap(result.first->second);
            }
        } else {
        	// value为空, 并且在associations中有记录,则进行擦除操作 
            auto refs_it = associations.find(disguised);
            if (refs_it != associations.end()) {
                auto &refs = refs_it->second;
                auto it = refs.find(key);
                if (it != refs.end()) {
                    association.swap(it->second);
                    refs.erase(it);
                    if (refs.size() == 0) {
                        associations.erase(refs_it);
                    }
                }
            }
        }
    }

    // release the old value (outside of the lock).
    association.releaseHeldValue();
}

所以我们通过这个插入的代码可以看出来存储关联对象的是hash表,代码关系如下图所示
在这里插入图片描述

图是从http://www.cocoachina.com/cms/wap.php?action=article&id=23892转过来的,感谢作者,侵权删除

从数据结构和插入代码我们看出,之所以关联可以在运行时向已存在类中添加成员变量的原因是因为,被添加的成员变量实际并没有存储在类的内存中,而是在这个树状结构中的某一张hash表中以key value pair的形式存储, 获取也根据key获取, 所以, 它为什么可以给category添加成员变量也就能解释通了

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐