iOS之关联对象

        我们在 iOS 开发中经常需要使用分类(Category),为已经存在的类添加属性的需求,但是使用@property 并不能在分类中正确创建实例变量和存取方法。

        不过,通过 Objective-C 运行时中的关联对象,也就是 Associated Object,我们可以实现上述需求。

分类中的 @property :@property 可以说是一个 Objective-C 编程中的“宏”,它有元编程的思想。

@interface Object : NSObject

@property (nonatomic, strong) NSString *property;

@end

在使用上述代码时会做三件事:

    1.生成实例变量 _property

    2.生成 getter 方法 - property

    3.生成 setter 方法 - setProperty:

@implementation Object{

NSString *_property;

}

- (NSString *)property {

return _property;

}

- (void)setProperty:(NSString *)property {

_property = property;

}

@end

        这些代码都是编译器为我们生成的,虽然你看不到它,但是它确实在这里,我们既然可以在类中使用。但分类中的 @property 并没有为我们生成实例变量以及存取方法,而需要我们手动实现。

使用关联对象

Q:我们为什么要使用关联对象?

A:因为在分类中 @property 并不会自动生成实例变量以及存取方法,所以一般使用关联对象为已经存在的类添加"属性"

下面,会介绍 ObjC 运行时为我们提供的与关联对象有关的 API,并在分类中实现一个伪属性

NSObject+AssociatedObject.h

@interface NSObject (AssociatedObject)

@property (nonatomic, strong) id associatedObject;

@end

NSObject+AssociatedObject.m

@implementation NSObject (AssociatedObject)

- (void)setAssociatedObject:(id)object {

objc_setAssociatedObject(self, @selector(associatedObject), object, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

}

- (id)associatedObject {

return objc_getAssociatedObject(self,  _cmd);

}

注:这里的 _cmd 代指当前方法的选择子,也就是 @selector(categoryProperty)。

我们使用了两个方法 objc_getAssociatedObject 以及 objc_setAssociatedObject 来模拟“属性"的存取方法,而使用关联对象模拟实例变量。

在这里需要解释下我们为什么会向方法中传入 @selector(categoryProperty)?

关于上面这个问题,我们须得看下方法的原型:

id objc_getAssociatedObject(id object, const void *key);

void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);

        从上面的方法原型中我们可以看出@selector(categoryProperty) 也就是参数中的 key,其实可以使用静态指针 static void * 类型的参数来代替,不过在这里,还是强烈推荐使用 @selector(categoryProperty) 作为 key 传入。因为这种方法省略了声明参数的代码,并且能很好地保证 key 的唯一性。(当然也避免了起名不会起的原因)

下来需要解释的一个就是OBJC_ASSOCIATION_RETAIN_NONATOMIC

关联对象的行为


而我们在代码中实现的属性 associatedObject 就相当于使用了 nonatomic 和 strong 修饰符。

说到上面2个方法一个设置和一个获取,那么哪有一个删除objc_removeAssociatedObjects

        你可能会在刚开始接触对象关联时想要尝试去调用objc_removeAssociatedObjects() 来进行删除操作,但如文档中所述,你不应该自己手动调用这个函数。此函数的主要目的是在“初试状态”时方便地返回一个对象。你不应该用这个函数来删除对象的属性,因为可能会导致其他客户对其添加的属性也被移除了。规范的方法是:调用objc_setAssociatedObject方法并传入一个nil 值来清除一个关联。

应用:

添加私有属性用于更好地去实现细节。当扩展一个内建类的行为时,保持附加属性的状态可能非常必要。

添加public属性来增强category的功能。有些情况下这种(通过关联对象)让category行为更灵活的做法比在用一个带变量的方法来实现更有意义。

创建一个用于KVO的关联观察者。当在一个category的实现中使用KVO时,建议用一个自定义的关联对象而不是该对象本身作观察者。


你可能感兴趣的:(iOS之关联对象)