Runtime objc4-779.1 为什么不能向一个已存在的类添加成员变量?有什么办法达到相同的效果?

Posted Jsen_Wang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Runtime objc4-779.1 为什么不能向一个已存在的类添加成员变量?有什么办法达到相同的效果?相关的知识,希望对你有一定的参考价值。

上一篇博客继续

第二种方法

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 associationpolicy, 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添加成员变量也就能解释通了

以上是关于Runtime objc4-779.1 为什么不能向一个已存在的类添加成员变量?有什么办法达到相同的效果?的主要内容,如果未能解决你的问题,请参考以下文章

Runtime objc4-779.1 为什么不能向一个已存在的类添加成员变量?有什么办法达到相同的效果?

Runtime objc4-779.1 App启动过程中Runtime都干了什么?

Runtime objc4-779.1 OC中,为什么swizzleMethod时要先addMethod?

Runtime objc4-779.1 App启动过程中Runtime都干了什么?

Runtime objc4-779.1 一图看懂iOS Runtime消息转发

Runtime objc4-779.1 通过runtime源码对OC对象销毁过程解析