- 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);
}
复制代码
- 根据源码, 可以讲这四个核心对象整理如下图
- 他们的关系如下
总结: 通过对象关联的成员变量, 在底层是被统一管理的, 并不是合并到了类对象的成员列表中