在《objc runtime (二)交换方法》中我提到过runtime最实用的就是交换方法和动态添加属性两个用法。现在就来说说如何动态添加属性。
添加属性
添加属性的本质就是让某个属性与某个对象产生一个关联。
一般情况下,添加属性都是在类文件中使用@property
来创建。
那如果我们拿不到类文件(系统类)呢?用分类吗?
但是在分类中使用@property
是不会自动实现getter
和setter
的,并且也不会生成-
开头的属性。
虽然我们可以利用静态变量来实现getter
和setter
。然而静态变量并不会随着对象的释放而释放,很显然这不是一个好办法。
动态添加属性
函数说明
runtime中有这样两个函数:
/**
* 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.”
*
*/
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);
/**
* 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.
*
*/
id objc_getAssociatedObject(id object, const void *key);
从函数名称我们可以看出两个函数的作用分别是添加属性对象和获取属性对象
从函数的定义中我们也很容易看出:
-
object
是要关联的对象; -
key
是关联的关键字; -
value
是对象对应关联的关键字的值; -
policy
是关联的策略;
对于policy
这个变量,我们查看类型objc_AssociationPolicy
的声明:
/**
* 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. */
};
可以看出,其实就是我们平常跟在@property
后面的weak
、strong
等等的类似策略,不过是换了一种方式而已。
具体实现
刚才已经说过,我们可以采用分类来为拿不到系统类添加属性。我们采取同样的方法,利用@property
添加后,利用runtime提供的函数来实现getter
和setter
。
举例说明:
假设我们要为NSObject
添加一个NSString
类型的属性addedProperty
。
分类代码如下:
-
创建分类
这里要给NSObject
添加属性,所以创建NSObject
的分类,取名为NSObject+Property
; - 利用
@property
声明
@interface NSObject (Property)
// 由于在setter中会设置添加策略,故这里没有写策略
@property NSString *addedProperty;
@end
- 实现
getter
和setter
@implementation NSObject (Property)
- (void)setAddedProperty:(NSString *)addedProperty{
objc_setAssociatedObject(self, "addedProperty", addedProperty, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)addedProperty{
return objc_getAssociatedObject(self, "addedProperty");
}
@end
- 调用验证
NSObject *obj = [[NSObject alloc]init];
obj.addedProperty = @"动态添加属性测试";
NSLog(@"%@", obj.addedProperty);
执行结果
2017-01-31 00:16:07.299420 runtimeDemo[37201:1584486] 动态添加属性测试
Program ended with exit code: 0