Category分类添加成员变量

思考:如何实现给分类“添加成员变量”?
默认情况下,因为分类底层结构的限制,不能添加成员变量到分类中。但可以通过关联对象来间接实现。

我们现在来一步步分析:如下

// RMPerson类
@interface RMPerson : NSObject
@property (nonatomic, assign) int age;
@end

@implementation RMPerson
@end

---------------------------------------------------

//RMPerson的分类
@interface RMPerson (Test)
//{
//    int _weight;
//}
@property (nonatomic, assign) int weight;
@end

@implementation RMPerson (Test)
@end

// -------------------------------------------------

#import "ViewController.h"
#import "RMPerson.h"
#import "RMPerson+Test.h"

@interface ViewController ()
@end

// ViewController控制器的实现
@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    RMPerson *person = [[RMPerson alloc] init];
    person.age = 10;
    person.weight = 30;
    
    RMPerson *person2 = [[RMPerson alloc] init];
    person2.age = 20;
    person2.weight = 40;
    
    NSLog(@"person-----  age : %d ,weight : %d",person.age,person.weight);
    NSLog(@"person2----- age : %d ,weight : %d",person2.age,person2.weight);
}

// 打印
 Terminating app due to uncaught exception 'NSInvalidArgumentException',
 reason: '-[RMPerson setWeight:]: unrecognized selector sent to instance 0x600003d1c4e0'

1.在大括号内直接声明成员变量_weight,编译器直接报错误Instance variables may not be placed in categories.所以分类中不能直接添加成员变量
2.在RMPerson+(Test)分类中只声明了属性,直接运行,报错:reason: '-[RMPerson setWeight:]: unrecognized selector sent to instance 0x600003d1c4e0',没有找到setWeight方法,那我们来在RMPerson+(Test).m中实现setWeight方法试试,是否就能添加属性了?
(注意:在分类中声明属性,只会生成setter、getter方法的声明,不会生成成员变量和setter、getter方法的实现)

RMPerson+Test.m
给属性weight添加set、get方法,因为分类不能添加成员变量,所以用_weitght接收会报错。那我们可以用什么方法取代成员变量_weight去接收setWeight方法传进来的值呢?
字典?全局变量?我们尝试下。

1.定义一个int的全局变量weight_替代成员变量_weight
int weight_;
@implementation RMPerson (Test)

// 1.定义一个int weight_替代成员变量_weight
- (void)setWeight:(int)weight {
    weight_ = weight;
}

- (int)weight {
    return weight_;
}

int的全局变量打印图.png
的确是能实现了,由于使用的是全局变量来替代成员变量,但在创建多个对象的时候,重新调用set方法时,会覆盖上一个对象的值,所以此方法是不可取的。
#####2.因为每个对象,其属性的值是不同的,存在一对一的关系,所以可以声明字典来实现

#import "RMPerson+Test.h"

NSMutableDictionary *weight_;
+ (void)load {
    weight_ = [NSMutableDictionary dictionary];
}

@implementation RMPerson (Test)
// 2.定义NSMutableDictionary字典_weight替代成员变量_weight
// 在load方法里创建weight_对象,因为load只加载一次
+ (void)load {
    weight_ = [NSMutableDictionary dictionary];
}

- (void)setWeight:(int)weight {
    // 为什么使用person对象的内存地址作为其key?因为其内存地址是唯一的
    NSString *key = [NSString stringWithFormat:@"%p",self]; 
    weight_[key] = @(weight);
}

- (int)weight {
    NSString *key = [NSString stringWithFormat:@"%p",self];
    return [weight_[key] intValue];
}

@end

再次打印看看结果,的确是实现了我们想要的结果。从表面上看没什么区别,但实际属性age属性weight两者的本质是有区别的,并且有一定的缺陷。所以,还有更好的处理方法来处理,使用关联对象。
#####缺陷:
1.本质上的区别

  • person.age 是存放在Person内存里面
  • person.weight 实际上是定义了一个全局的字典对象里面

2.因为使用全局字典对象存储时,当创建多个对象时,不在同一线程状态下访问set或get方法,会造成同时访问的安全性问题

3.关联对象
// 3.关联对象
- (void)setName:(NSString *)name {
    objc_setAssociatedObject(self, @"name", name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (NSString *)name {
    return objc_getAssociatedObject(self, @"name");
}

//关联对象key的常见用法
static void *MyKey = &MyKey;
objc_setAssociatedObject(obj, MyKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
objc_getAssociatedObject(obj, MyKey)

static char MyKey;
objc_setAssociatedObject(obj, &MyKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
objc_getAssociatedObject(obj, &MyKey)

使用属性名作为key
objc_setAssociatedObject(obj, @"property", value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_getAssociatedObject(obj, @"property");

使用get方法的@selecor作为key
objc_setAssociatedObject(obj, @selector(getter), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
objc_getAssociatedObject(obj, @selector(getter))
综合上述的三种方法,使用关联对象最优,使用关联对象更好的间接添加成员变量。

本文源码可以在这里获得:https://github.com/476455183/OC-SourceCode

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值