如何给Category增加属性

前言

本文已经添加到专辑:《彻底弄懂OC》。 欢迎加入我的QQ群:661461410,一起探讨iOS底层原理。

相关问题

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

背景知识

我们知道在一个类中增加一个属性,编译器会帮我们做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 <objc/runtime.h>

@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_setAssociatedObjectobjc_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

其实现结构如下:

关联对象实现原理

其中 AssociationsHashMapdisguised_ptr_t 表示对象,AssociationMap 中的 void * 表示 keyObjectAssociation 中包含value和存储策略。

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

实现原理

总结

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

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

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

互动交流

关注公众号
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值