在《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
网友评论
一般添加是指在静态编辑文件的时候直接将属性写到接口文件上
运行时那种严格来说是“使关联”而不是添加。
而且这个被关联的对象无法被当成是实例变量来看的。
我同意你评论中所说的内容,在文章开头我就交代了添加属性的本质,这里写的动态添加属性也就是利用runtime达到一样的效果,并不是真正的添加属性。
希望之后多交流,欢迎指正。