OC对象 - 关联对象(如何给分类添加成员变量)

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_ASSIGNassign
OBJC_ASSOCIATION_RETAIN_NONATOMICstrong, nonatomic
OBJC_ASSOCIATION_COPY_NONATOMICcopy, nonatomic
OBJC_ASSOCIATION_RETAINstrong, atomic
OBJC_ASSOCIATION_COPYcopy, 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,这就相当于把多个属性都与NULLKey关联,明显就出问题了

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看名字是一个HashMap,我们可以认为是 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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值