Swift教程

如何给Category增加属性

本文主要是介绍如何给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对象为例,图示一下整体的结构。

实现原理

总结

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

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

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

互动交流

关注公众号
这篇关于如何给Category增加属性的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!