一、本文解答的问题
1. 为什么类
中添加属性可以赋值取值
,而分类
中添加的属性却不能直接赋值取值
呢?
- 假设添加属性的代码是
@property(nonatomic, assign) int age;
-
类
中添加属性等价于做了三件事:①添加成员变量_age
②声明 age
和 setAge:
方法 ③添加 age
和 setAge:
的方法实现
-
分类
中添加属性等价于只做了一件事:声明 age
和 setAge:
方法,没有成员变量,也没有方法实现
- 所以我们在分类中使用分类的属性,将会得到如下错误
'-[Person setAge:]: unrecognized selector sent to instance'
2. 不使用Objective-C提供的objc_setAssociatedObject
相关 API 我们能自己实现关联对象的效果吗?如果有,有哪些思路?
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;
}