一、介绍
Objective-C 中,可以动态为一个对象关联一个或几个数值,以便完成一些特殊需求。主要涉及到以下几个函数:(完整声明见文尾附件内容部分)
1、objc_setAssociatedObject
- 用途:为对象指定一个关联对象
- 参数说明:
参数名 | 参数类型 | 作用 |
---|---|---|
object | id |
指定要为哪个对象增加关联值 |
key | const void * |
为要关联的值指定 key |
value | id |
要关联的值 |
policy | objc_AssociationPolicy |
关联方式,copy 、retain 、assign 、nonatomic 等 |
- 返回值:无
2、objc_getAssociatedObject
- 用途:获取对象的一个关联值
- 参数说明:
参数名 | 参数类型 | 作用 |
---|---|---|
object | id |
指定关联了值的对象 |
key | const void * |
关联值的 key |
- 返回值:关联的值,类型为 id
二、举例
#import <objc/runtime.h>
// 指定 Key
void *p = "key";
// 为对象指定值
objc_setAssociatedObject(obj, p, [@"xyz" stringByAppendingString:@"123"], OBJC_ASSOCIATION_COPY_NONATOMIC);
// 获取指定的值
id myval = objc_getAssociatedObject(obj, p);
NSLog(@"val = %@", myval);
☆☆☆重要说明☆☆☆
其中的 Key 是一个无类型指针,函数会根据这个作为 Key 来进行数值读写。举例中,使用了一个 C 字符串作为标识,只是为了方便,但是容易引起一个误会,认为是根据字符串内容作为 Key,实际不是这样的!!!实际上是以指针中所存放的地址(可以认为是一个整数)作为 Key 的。
三、应用场景
实际开发过程中,Category 是个很方便的扩展已有类的机制。但是美中不足,Category 不被允许有自己的成员变量,这在做扩展时总有些不太方便。通过这种方式,可以为 Category 增加自己的成员值。
#import <objc/runtime.h>
// 接口
@interface MyClass (CategoryProperty)
@property (nonatomic) NSInteger intVal;
- (void)showIntVal;
@property (nonatomic, copy) NSString *strVal;
- (void)showStrVal;
@end
// 实现
@implementation MyClass (CategoryProperty)
- (void)setIntVal:(NSInteger)intVal_ {
objc_setAssociatedObject(self, @selector(intVal), @(intVal_), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSInteger)intVal {
return ((NSNumber *)objc_getAssociatedObject(self, @selector(intVal))).integerValue;
}
- (void)showIntVal {
NSLog(@"intVal = %ld", self.intVal);
}
- (void)setStrVal:(NSString *)strVal_ {
objc_setAssociatedObject(self, @selector(strVal), strVal_, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)strVal {
return (NSString *)objc_getAssociatedObject(self, @selector(strVal));
}
- (void)showStrVal {
NSLog(@"strVal = %@", self.strVal);
}
说明:
上面例子中的 Key 参数,这里采用了类似 @selector(strVal)
的形式。strVal
是属性值的“读方法”,方法的地址肯定不会重复,所以通过方法的 SEL
指针作为 Key 还是比较科学和安全的。
在 AFNetworking 3.x 中,UIWebView+AFNetworking 这个 Category 中,就有类似的使用。
四、附件
相关部分完整声明:在 runtime.h
中
#import <objc/runtime.h>
/* Associative References */
/**
* Policies related to associative references.
* These are options to objc_setAssociatedObject()
*/
typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
OBJC_ASSOCIATION_ASSIGN = 0, /**< Specifies a weak reference to the associated object. */
OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, /**< Specifies a strong reference to the associated object.
* The association is not made atomically. */
OBJC_ASSOCIATION_COPY_NONATOMIC = 3, /**< Specifies that the associated object is copied.
* The association is not made atomically. */
OBJC_ASSOCIATION_RETAIN = 01401, /**< Specifies a strong reference to the associated object.
* The association is made atomically. */
OBJC_ASSOCIATION_COPY = 01403 /**< Specifies that the associated object is copied.
* The association is made atomically. */
};
/**
* Sets an associated value for a given object using a given key and association policy.
*
* @param object The source object for the association.
* @param key The key for the association.
* @param value The value to associate with the key key for object. Pass nil to clear an existing association.
* @param policy The policy for the association. For possible values, see “Associative Object Behaviors.”
*
* @see objc_setAssociatedObject
* @see objc_removeAssociatedObjects
*/
OBJC_EXPORT void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0);
/**
* Returns the value associated with a given object for a given key.
*
* @param object The source object for the association.
* @param key The key for the association.
*
* @return The value associated with the key \e key for \e object.
*
* @see objc_setAssociatedObject
*/
OBJC_EXPORT id objc_getAssociatedObject(id object, const void *key)
OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0);
/**
* Removes all associations for a given object.
*
* @param object An object that maintains associated objects.
*
* @note The main purpose of this function is to make it easy to return an object
* to a "pristine state”. You should not use this function for general removal of
* associations from objects, since it also removes associations that other clients
* may have added to the object. Typically you should use \c objc_setAssociatedObject
* with a nil value to clear an association.
*
* @see objc_setAssociatedObject
* @see objc_getAssociatedObject
*/
OBJC_EXPORT void objc_removeAssociatedObjects(id object)
OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0);
(完)