- Category中可以添加属性、协议、方法等, 但是并不能添加成员变量, 根据Category在底层的结构也可以发现,并没有存放成员变量的地方

一、准备代码
- 定义
Person类, 继承自NSObject, 有一个int类型age属性

- 定义
Person类的分类Person+Test1, 有两个属性, 分别是int类型的weight和NSString类型的name

- 类中的属性, 实际上会生成一个
成员变量, 并实现set和get方法, 所以Person中的age属性, 实际等于下面的代码

- 而
Category中是不能直接添加成员变量的

- 如果想要给
Category添加成员变量, 那么就需要使用runtime提供的关联对象
二、关联对象相关方法
- 添加关联对象
void objc_setAssociatedObject(id object, const void * key, id value, objc_AssociationPolicy policy)
- 获得关联对象
id objc_getAssociatedObject(id object, const void * key)
- 移除所有的关联对象
void objc_removeAssociatedObjects(id object)
- objc_AssociationPolicy
| 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 |
三、关联成员变量, 有四种key的取值方式
1、char *做为key值
- 关联对象方法中,
key的类型是const void *, 所以传入一个char *类型的数据就可以了

2、void *做为key值

3、字符串字面量做为key值
OC中字符串字面量存放在内存中的常量区, 所以相同字符串字面量的地址是相同的

4、@selecter(方法名)做为key值
- 同一个方法的
@selector(方法名), 地址是相同的, 所以可以直接作为关联对象的key值

- OC中提供了一个关键字
_cmd, 它的值就是当前方法的方法名, 所以上面的代码中, 可以如下修改 - 在
-(void)setName:(NSString *)name方法中,_cmd的值就是@selector(setName:), 在-(NSString *)name中,_cmd的值就是@selector(name)

- 在
main.m中使用Person类

- 可以看到,
name和weight两个关联对象可以和age一样正常使用
四、关联对象的原理
-
实现关联对象技术的核心对象有四个
- AssociationsManager
- AssociationsHashMap
- ObjectAssociationMap
- ObjcAssociation
-
其中的
AssociationsHashMap和ObjectAssociationMap我们可以看做是OC里面的字典 -
下载并打开OC的底层源码, 搜索
objc_setAssociatedObject -
在这里可以看到
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)和id objc_getAssociatedObject(id object, const void *key)

- 进入
_object_set_associative_reference函数中, 可以看到代码AssociationsManager manager;

- 查看一下
AssociationsManager的数据结构, 可以看到其中包含着AssociationsHashMap

- 进入到
AssociationsHashMap中, 之前说过AssociationsHashMap类似于OC中的字典, 所以他也有key和value AssociationsHashMap的key是disguised_ptr_t,value是ObjectAssociationMap *

- 继续查看
ObjectAssociationMap的数据结构, 它也类似于OC的字典, 其中的key是<void *,value是ObjcAssociation

- 在
ObjcAssociation中有两个成员变量, 分别是uintptr_t _policy;和id _value;

- 用代码举例,
uintptr_t _policy;和id _value;存储的分别是下面代码的OBJC_ASSOCIATION_COPY_NONATOMIC和name, 即第四个参数和第三个参数的值
- (void)setName:(NSString *)name
{
objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- 根据源码, 可以讲这四个核心对象整理如下图

- 他们的关系如下

总结: 通过对象关联的成员变量, 在底层是被统一管理的, 并不是合并到了类对象的成员列表中