必须为元素类型 association 声明属性 oftype。_如何给Category添加属性

相关问题

  • 分类可以添加属性吗? 如果可以,应该如何实现。

背景知识

我们知道在一个类中增加一个属性,编译器会帮我们做3件事,比如,我们给Person这个类增加一个属性age,编译之后,类中会增加一个成员变量_age, 增加get方法和set方法的生命与实现 -(int)age 和 -(void)setAge:(int)age。

但是我们在分类里面声明一个属性,会帮我们声明两个方法set, get。 但不会生成实现,也不会产生成员变量。所以我们不能直接给Category添加成员变量,但是可以间接实现Category有成员变量的效果。

实现分类添加属性

使用全局变量

下面代码中,我们为person的分类增加了一个age属性,我们通过声明一个全局变量_age,将age的值存储到_age中。

@interface Person (Test)@property (nonatomic, assign) int age;@end  @implementation Person (Test)int _age;- (void)setAge:(int)age {    _age = age;}-(int)age {    return _age;}@end

但,这种方式有致命的确定,就是多个实例对象共用_age,导致数据错误。

使用字典
#import "Person+Test.h"@implementation Person (Test)NSMutableDictionary *_ageDic;+ (void)load {    _ageDic = [[NSMutableDictionary alloc] init];}- (int)age {    NSString *selfKey = [NSString stringWithFormat:@"%p", self];    return [[_ageDic valueForKey: selfKey] intValue];}- (void)setAge:(int)age {    NSString *selfKey = [NSString stringWithFormat:@"%p", self];    [_ageDic setValue:@(age) forKey:selfKey];}@end

使用字典来存储值解决上一种方法中多个对象导致数据错乱的问题,但它也有一些问题,如下:

  1. 多线程问题。
  2. 每增加一个属性都要新增一个字段,拓展性太差,假如再加一个属性name,就需要增加一个字段用来存储对象对应的name值。
  3. 属性是存储在字段内部的,并不是存储在类的对象内部。
关联对象
#import "Person+Test.h"#import @implementation Person (Test)- (int)age {    return [objc_getAssociatedObject(self, @selector(age)) intValue];}- (void)setAge:(int)age {    objc_setAssociatedObject(self, @selector(age), @(age), OBJC_ASSOCIATION_ASSIGN);}@end

这种方式,我们使用runtime提供的两个方法objc_setAssociatedObject 和 objc_setAssociatedObject 来分别设置和获取属性值。就objc_setAssociatedObject而言其接受三个参数:

  1. id,表示为当前对象关联属性。
  2. key,关联属性对应的唯一key,这里我们使用get方法的函数地址值。
  3. value,key对应的值。
  4. objc_AssociationPolicy, 存储策略。有5个取值,根据属性的类型选择对应的内存存储策略。

OBJC_ASSOCIATION_ASSIGN = 0,

OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1,

OBJC_ASSOCIATION_COPY_NONATOMIC = 3,

OBJC_ASSOCIATION_RETAIN = 01401,

OBJC_ASSOCIATION_COPY = 01403

关联对象实现原理

关联对象的底层实现涉及到了4个类,分别是:

  • AssociationsManager
  • AssociationsHashMap
  • ObjectAssociationMap
  • ObjcAssociation

其实现结构如下:

9b5ca3eb2bb3505b7b58fd43353dce9e.png

关联对象实现原理

其中 AssociationsHashMap中 disguised_ptr_t 表示对象,AssociationMap 中的 void * 表示 key。ObjectAssociation 中包含value和存储策略。

我们以上面的Person对象为例,图示一下整体的结构。

a49eeebc416bafdcad03b0420576cfcd.png

实现原理

总结

通过上面的分析,我们回答一下开头的问题:

分类不可以直接添加属性,但可以间接添加,最优雅的方式是通过关联对象进行属性与分类的绑定。

那么,留一个问题,你认为关联对象需要手动释放吗? 在类销毁的时候,需要释放其通过关联对象绑定的属性吗?

交流互动

如果你对探究OC底层原理有兴趣,欢迎加入我的交流群,一起探讨、学习、进步!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值