美文网首页
分类的属性

分类的属性

作者: 三国韩信 | 来源:发表于2020-06-17 15:40 被阅读0次

    众所周知,在分类中可以定义属性,但是它不会自动生产settet/getter方法,也不会生成对应的实例变量。所以在分类中使用属性的话,需要自己去实现settet/getter方法。在setter和getter方法中,使用关联对象的方法来存储属性的值。

    #import "LGPerson.h"
    @interface LGPerson (LG)        //LGPerson的分类。
    @property (nonatomic, copy) NSString *cate_name;
    @end
    
    @implementation LGPerson (LG)
    -(void)setCate_name:(NSString *)cate_name{    // setter方法
        /**
        参数一:id object : 给哪个对象添加属性,这里要给自己添加属性,用self。
        参数二:void * == id key : 属性名,根据key获取关联对象的属性的值,在objc_getAssociatedObject中通过次key获得属性的值并返回。
        参数三:id value : 关联的值,也就是set方法传入的值给属性去保存。
        参数四:objc_AssociationPolicy policy : 策略,属性以什么形式保存。
        */
        objc_setAssociatedObject(self, @"name",cate_name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    
    -(NSString *)cate_name{     // getter方法
        /**
        参数一:id object : 获取哪个对象里面的关联的属性。
        参数二:void * == id key : 什么属性,与objc_setAssociatedObject中的key相对应,即通过key值取出value。
        */
        return objc_getAssociatedObject(self, @"name");
    }
    

    那么关联对象objc_setAssociatedObject 和 objc_getAssociatedObject底层是怎么实现的呢? show you code:

    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 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);
    }
    
    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;
        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) {
                        objc_retain(value);
                    }
                }
            }
        }
        if (value && (policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE)) {
            objc_autorelease(value);
        }
        return value;
    }
    

    通过底层的源码我们可以看出,关联对象是通过AssociationsManager来管理
    AssociationsHashMap,从而来存储属性的值的。
    当我们通过关联对象来存值的时候,其流程为:

    1. 通过AssociationsManager获取到全局的AssociationsHashMap
    2. 首先判断属性的value是否存在,若为nil,那么则表示要把之前存的值清空。(这里会有一个查找的流程,找到旧值,把它干掉,如果没有旧值,则啥都不干。)
    3. 通过对象做为key去find当前self所管理的属性的ObjectAssociationMap。
    • 3.1. 如果能找到当前对象的ObjectAssociationMap,则在ObjectAssociationMap中通过属性的name做为key,去找ObjectAssociation对象,如果找到了,就修改之前的ObjectAssociation对象中的value和policy的值。如果没有找到,则说明第一次存,就新建一个ObjectAssociation对象,并把它关联到ObjectAssociationMap中去。
    • 3.2. 如果未能找到当前对象的ObjectAssociationMap,则新建一个ObjectAssociationMap对象和ObjectAssociation对象,把value和policy的值存进去,并把ObjectAssociationMap关联到AssociationsHashMap中去。

    当我们通过关联对象来取值的时候,其过程和存值的情况类似,也是一个查找的流程,这里具体就不赘述了。

    关联对象.png

    图片来自网络上的大神总结的,侵权必删。

    最后,关联对象的ObjectAssociationMap,以及ObjectAssociation是什么时候被remove的呢,答案就是当对象被delloc的时候,delloc的时候,会去查找当前对象是否有关联的属性,有的话,就会把对应的内容都删除掉。

    相关文章

      网友评论

          本文标题:分类的属性

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