美文网首页iOS 开发 Objective-C 分类
iOS 底层 day07 Category上的关联对象

iOS 底层 day07 Category上的关联对象

作者: 望穿秋水小作坊 | 来源:发表于2020-08-29 17:28 被阅读0次

    一、本文解答的问题

    1. 为什么中添加属性可以赋值取值,而分类中添加的属性却不能直接赋值取值呢?
    • 假设添加属性的代码是 @property(nonatomic, assign) int age;
    • 中添加属性等价于做了三件事:①添加成员变量_age ②声明 agesetAge: 方法 ③添加 agesetAge:的方法实现
    • 分类 中添加属性等价于只做了一件事:声明 agesetAge: 方法,没有成员变量,也没有方法实现
    • 所以我们在分类中使用分类的属性,将会得到如下错误'-[Person setAge:]: unrecognized selector sent to instance'
    2. 不使用Objective-C提供的objc_setAssociatedObject 相关 API 我们能自己实现关联对象的效果吗?如果有,有哪些思路?
    • 可以
    • 有问题3,4两种思路
    3. 观察下面代码,实现了关联对象效果,能否正常使用?有哪些缺点?可以优化吗?
    // Person+Test1.h 
    #import "Person.h"
    @interface Person (Test1)
    @property(nonatomic, assign) int age;
    @end
    
    // Person+Test1.m
    #import "Person+Test1.h"
    static int age_;
    
    @implementation Person (Test1)
    - (void)setAge:(int)age {
        age_ = age;
    }
    - (int)age {
        return age_;
    }
    @end
    
    • 能使用,但是有几个缺点
    • ①所有的Person的实例变量,都是使用同一个关联对象,设置和取值都会相互被影响
    • ②存在线程安全问题
    • ③写法比较麻烦,分类每次增加属性,都需要额外增加静态变量来存值
    • ④类销毁,age_未释放,所以存在内存泄露问题
    • 优化如下,看问题4
    4. 我们对上面的代码优化一下,观察下面代码,实现了关联对象效果,能否正常使用?有哪些缺点?可以优化吗?
    #import "Person+Test1.h"
    #define SPKey [NSString stringWithFormat:@"%p",self]
    static NSMutableDictionary *ageDic;
    @implementation Person (Test1)
    + (void)load {
        ageDic = [[NSMutableDictionary alloc] init];
        
    }
    - (void)setAge:(int)age {
        [ageDic setObject:@(age) forKey:SPKey];
    }
    - (int)age {
        return [[ageDic valueForKey:SPKey] intValue];
    }
    @end
    
    • 上述代码更加完善了,Person的实例对象之间不会相互影响,能够达到关联对象的效果
    • 依然还有的缺点是:
    • ①写法比较繁琐
    • ②存在线程安全问题
    • ③类销毁,ageDic未释放,所以存在内存泄露问题
    5. 用Objective-C提供的objc_setAssociatedObject 相关 API ,实现关联对象怎么做?
    #import "Person+Test1.h"
    #import <objc/runtime.h>
    @implementation Person (Test1)
    - (void)setAge:(int)age {
        objc_setAssociatedObject(self, @selector(age), @(age), OBJC_ASSOCIATION_ASSIGN);
    }
    - (int)age {
        return [objc_getAssociatedObject(self, @selector(age)) intValue];
    }
    @end
    
    • 代码简洁
    • 如果类销毁,关联对象也会被擦除
    • 有内存管理策略 policy ,线程相对安全些
    6. 关联对象的实现原理图解
    关联对象的实现原理图解

    二、关联对象源码解读

    1. 掌握四个核心类
    • AssociationsManager
    • AssociationsHashMap
    • ObjectAssociationMap
    • ObjcAssociation
    2. 核心源码
    class AssociationsManager {
        static AssociationsHashMap *_map;
    }
    
    class AssociationsHashMap : public unordered_map<disguised_ptr_t, ObjectAssociationMap *>
    
    class ObjectAssociationMap : public std::map<void *, ObjcAssociation>
    
    class ObjcAssociation {
            uintptr_t _policy;
            id _value;
    }
    
    

    相关文章

      网友评论

        本文标题:iOS 底层 day07 Category上的关联对象

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