美文网首页
iOS 关联对象

iOS 关联对象

作者: 小老弟码代码 | 来源:发表于2020-10-13 09:16 被阅读0次

    在平时的工作中经常碰到给类别添加属性的操作,那么实现思路是怎么样的呢?

    代码实现:新建一个Person类和Person+Text的类别

    //Person 代码
    #import <Foundation/Foundation.h>
    @interface Person : NSObject
    @property (assign, nonatomic) int age;
    @end
    
    
    //类别代码
    #import "Person.h"
    @interface Person (Test)
    @property (copy, nonatomic) NSString *name;
    @end
    
    //调用代码
    Person *person = [[Person alloc] init];
    person.age = 10;
    person.name = @"jack";
    NSLog(@"person - age is %d, name is %@", person.age, person.name);
    
    输出:age 10,name 没有值
    
    name赋值失败原因:age是类里面的属性,系统会自动生成成员变量_agegetAgesetAge方法的声明和实现。所以赋值成功。name是利用类别添加的属性,在类别里面添加属性并不会生成_name成员变量,只会getNamesetName方法的声明,没有实现。所以赋值失败。
    本质原因:Category 结构体,并没有存储成员变量
    struct category_t {
        const char *name;
        classref_t cls;
        struct method_list_t *instanceMethods; // 对象方法
        struct method_list_t *classMethods; // 类方法
        struct protocol_list_t *protocols; // 协议
        struct property_list_t *instanceProperties; // 属性
        // Fields below this point are not always present on disk.
        struct property_list_t *_classProperties;
    
        method_list_t *methodsForMeta(bool isMeta) {
            if (isMeta) return classMethods;
            else return instanceMethods;
        }
    
        property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
    };
    
    实现类别添加属性思路1,设置一个全局字典自己保存成员变量的值,代码实现如下。
    #import "Person+Test.h"
    
    #define Key [NSString stringWithFormat:@"%p", self]
    
    @implementation Person (Test)
    
    NSMutableDictionary *names_;
    + (void)load{
        names_ = [NSMutableDictionary dictionary];
    }
    - (void)setName:(NSString *)name{
        names_[Key] = name;
    }
    - (NSString *)name{
        return names_[Key];
    }
    @end
    

    通过这种思路确实可以实现给类别添加属性的功能,但是也有明显的弊端。

    • 1.每次给这个添加一个新的属性时需要重新创建一个新的字典保存。
    • 2.给属性赋值或者取值时会出现线程完全问题,需要加锁控制。
    • 3.字典什么时候释放,也存在内存泄漏的隐患。
    如果使用上述思路维护难度较大,使用runtime关联对象方法,代码如下
    - (void)setName:(NSString *)name
    {
        objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY_NONATOMIC);
    }
    
    - (NSString *)name
    {
        // 隐式参数
        // _cmd == @selector(name)
        return objc_getAssociatedObject(self, _cmd);
    }
    
    1. 动态添加属性
    //id  _Nonnull object  关联的对象
    //const void * _Nonnull key 存储的key
    //id  _Nullable value 存储的value
    //objc_AssociationPolicy policy 对应的修饰符
    objc_setAssociatedObject(<#id  _Nonnull object#>, <#const void * _Nonnull key#>, <#id  _Nullable value#>, <#objc_AssociationPolicy policy#>)
    

    参数一:id object : 给哪个对象添加属性,这里要给自己添加属性,用self
    参数二:void * == id key : 属性名,根据key获取关联对象的属性的值,在objc_getAssociatedObject中通过次key获得属性的值并返回。
    参数三:id value :关联的值,也就是set方法传入的值给属性去保存。
    参数四:objc_AssociationPolicy policy : 策略,属性以什么形式保存。
    策略有有以下几种

    typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
        OBJC_ASSOCIATION_ASSIGN = 0,  // 指定一个弱引用相关联的对象
        OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, // 指定相关对象的强引用,非原子性
        OBJC_ASSOCIATION_COPY_NONATOMIC = 3,  // 指定相关的对象被复制,非原子性
        OBJC_ASSOCIATION_RETAIN = 01401,  // 指定相关对象的强引用,原子性
        OBJC_ASSOCIATION_COPY = 01403     // 指定相关的对象被复制,原子性   
    };
    

    策略对应的属性修饰符图示


    image.png

    key值只要是一个指针即可,我们可以传入@selector(name)

    2.获取属性

    objc_getAssociatedObject(id object, const void *key);
    

    参数一:id object : 获取哪个对象里面的关联的属性。
    参数二:void * == id key :什么属性,与objc_setAssociatedObject中的key相对应,即通过key值取出value
    3.移除所有关联对象

    - (void)removeAssociatedObjects{
        // 移除所有关联对象
        objc_removeAssociatedObjects(self);
    }
    

    相关文章

      网友评论

          本文标题:iOS 关联对象

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