文章目录
- OC对象 - 关联对象(如何给分类添加成员变量)
- 1. 基本使用
- 1.1 提供的API
- 1.1.1 添加关联对象
- 1.1.2 获得关联对象
- 1.1.3 移除所有关联对象
- 1.1.3 修饰符
- 1.2 使用方法
- 1.2 Key的常见用法
- 1.2.1 使用的`get`方法的`@selecor`作为key
- 1.2.2 使用指针的地址作为key
- 1.2.3 使用static字符作为key
- 1.2.4 使用属性名作为key
- 1.3 错误用法举例
- 2. 实现原理
- 2.1 核心对象
- 2.2 核心对象之间的关联
- 2.2.1 示意图
- 扩展
- 如果setValue传了nil
- 源码
- 没有 weak
OC对象 - 关联对象(如何给分类添加成员变量)
Category(分类)的底层结构中,没有成员变量(ivar),因此不能给分类添加成员变量;在分类里面声明的属性,只会生成 get/set 方法的声明,没有方法的实现
所以我们不能直接给分类添加成员变量,但是可以间接实现,那就是使用关联对象
1. 基本使用
1.1 提供的API
1.1.1 添加关联对象
void objc_setAssociatedObject(id object, const void * key, id value, objc_AssociationPolicy policy)
1.1.2 获得关联对象
id objc_getAssociatedObject(id object, const void * key)
1.1.3 移除所有关联对象
void objc_removeAssociatedObjects(id object)
1.1.3 修饰符
objc_AssociationPolicy | 对应的修饰符 |
---|---|
OBJC_ASSOCIATION_ASSIGN | assign |
OBJC_ASSOCIATION_RETAIN_NONATOMIC | strong, nonatomic |
OBJC_ASSOCIATION_COPY_NONATOMIC | copy, nonatomic |
OBJC_ASSOCIATION_RETAIN | strong, atomic |
OBJC_ASSOCIATION_COPY | copy, atomic |
1.2 使用方法
- 创建
ZSXPerson
类,以及它的分类ZSXPerson+Text
ZSXPerson+Text.h
中声明想要添加的属性ZSXPerson+Text.m
中使用关联对象
API实现 get/set 方法
ZSXPerson类:
@interface ZSXPerson : NSObject
@end
@implementation ZSXPerson
@end
ZSXPerson+Test.h
@interface ZSXPerson (Test)
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) int weight;
@end
ZSXPerson+Test.m
#import "ZSXPerson+Test.h"
#import <objc/runtime.h>
@implementation ZSXPerson (Test)
- (void)setName:(NSString *)name {
objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)name {
// 隐式参数: _cmd,相当于当前方法的@selecor。(还有 self 也是隐式参数)
return objc_getAssociatedObject(self, _cmd);
}
- (void)setWeight:(int)weight {
objc_setAssociatedObject(self, @selector(weight), @(weight), OBJC_ASSOCIATION_ASSIGN);
}
- (int)weight {
return [objc_getAssociatedObject(self, _cmd) intValue];
}
@end
- 初始化两个实例对象,给属性设置不同的值,然后打印属性值
可以看到我们像正常使用对象的属性一样,使用分类中创建的属性
1.2 Key的常见用法
我们上面的示例,使用的get
方法的@selecor
来作为Key,这种写法相对比较简洁
1.2.1 使用的get
方法的@selecor
作为key
- objc_setAssociatedObject(obj, @selector(getter), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
- objc_getAssociatedObject(obj, @selector(getter))
1.2.2 使用指针的地址作为key
static void *MyKey = &MyKey;
- objc_setAssociatedObject(obj, MyKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
- objc_getAssociatedObject(obj, MyKey)
1.2.3 使用static字符作为key
static char MyKey;
- objc_setAssociatedObject(obj, &MyKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
- objc_getAssociatedObject(obj, &MyKey)
1.2.4 使用属性名作为key
- objc_setAssociatedObject(obj, @“property”, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
- objc_getAssociatedObject(obj, @“property”);
1.3 错误用法举例
static void *MyKey;
- objc_setAssociatedObject(obj, MyKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
- objc_getAssociatedObject(obj, MyKey)
如上写法,没有给*MyKey,他相当于是NULL
,这时候,如果再有另一个属性使用Keystatic void *OtherKey;
,*MyKey
和*OtherKey
都是 NULL
,这就相当于把多个属性都与NULL
Key关联,明显就出问题了
2. 实现原理
学习OC对象本质
时,我们知道对象实际被转化成struct ClassName_IMPL
结构体,对象的成员变量的值保存在该结构体中,例如:
struct ZSXStuden_IMPL {
struct NSObject_IMPL NSObject_IVARS;
int weight
};
使用关联对象
给分类添加的成员变量,他并不是保存在该结构体下,因为Category(分类)的底层结构中并没有ivar
。
他是存储在全局的统一的一个AssociationsManager
中
2.1 核心对象
- AssociationsManager
- AssociationsHashMap
- ObjectAssociationMap
- ObjcAssociation
2.2 核心对象之间的关联
关联对象是通过这四个核心对象共同完成的,他们之间的关系是这样的
- AssociationsManager 里面是一个
AssociationsHashMap
类型的成员map
AssociationsHashMap
看名字是一个HashMa
p,我们可以认为是iOS
中的字典
,这个字段的key
是被添加关联对象的对象的地址
,value
是个ObjectAssociationMap
ObjectAssociationMap
一样认为是个字典,key 是我们调用objc_setAssociatedObject
时传入的key
,value 是ObjectAssociation
ObjectAssociation
里面有两个成员,_policy是我们设置的修饰符(比如:OBJC_ASSOCIATION_ASSIGN
),_value是我们传入的值value
2.2.1 示意图
扩展
如果setValue传了nil
如果这么操作person.name = nil
,相当于person的name
这个关联对象,就是擦除如下图的数据
源码
没有 weak
objc_AssociationPolicy
的值并没有weak
,这点我们在使用的时候需要注意
@oubijiexi