分类是开发中经常需要使用的,但是短板就在于能够给分类添加属性,却不能自动生成属性的setter和getter,所以在运行时,会报错找不到setter和getter方法而直接崩溃。这时候,Runtime就可以解决这个短板。
废话不多说,先上代码:
我们先创建一个UIView的分类:UIView+Extension
//.h文件
#import <UIKit/UIKit.h>
@interface UIView (Extension)
@property (nonatomic,strong) NSString* value;
@end
//.m文件
#import "UIView+Extension.h"
#import <objc/runtime.h>
@implementation UIView (Extension)
- (void)setValue:(NSString *)value {
objc_setAssociatedObject(self, @selector(value), value, OBJC_ASSOCIATION_RETAIN);
}
- (NSString *)value {
return objc_getAssociatedObject(self, _cmd);//_cmd代表本方法的名称
}
@end
简单吧,接下来就是直接在需要使用的地方import
进去即可,然后就可以当做一个普通的属性使用了。
关于其原理,实际上就是利用了我们的属性的getter
和setter
方法,在方法中用Runtime为对象动态添加属性,也就是添加一个地址和值的关联,通过objc_setAssociatedObject
进行关联设置,通过objc_getAssociatedObject
进行取值的操作。
关于Associated Objects
,就以下三个相关方法
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);
id objc_getAssociatedObject(id object, const void *key);
void objc_removeAssociatedObjects(id object);
① 其中,void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);
这个方法用于给对象添加关联对象,它有四个参数:
id object
:需要添加关联的源对象
const void *key
:关联时的用来标记是哪一个属性的key(因为你可能要添加很多属性)
id value
:关联的对象
objc_AssociationPolicy policy
:关联策略
值得一提的是key
参数,常见的做法有以下几种:
//利用静态变量地址唯一不变的特性
1、static void *theKey = &theKey;
2、static NSString *theKey = @"theKey";
3、static char theKey;
但是我个人最喜欢的,还是上面的代码中使用的方法,直接取@selector
,用getter
方法名做key,省掉了一个变量名,够优(zhuang)雅(bi)。
② 第二个id objc_getAssociatedObject(id object, const void *key);
显而易见是根据key
获取值。
③ 最后一个void objc_removeAssociatedObjects(id object);
方法是移除关联的,很少用到,也不建议使用,因为这个函数会移除一个对象的所有关联对象,将该对象恢复成“原始”状态。这样做就很有可能把别人添加的关联对象也一并移除,这并不是我们所希望的。所以一般的做法是通过给 objc_setAssociatedObject
函数传入 nil
来移除某个已有的关联对象。
关联策略
objc_AssociationPolicy policy
是一个枚举值,包括:
typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
OBJC_ASSOCIATION_ASSIGN = 0,
OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1,
OBJC_ASSOCIATION_COPY_NONATOMIC = 3,
OBJC_ASSOCIATION_RETAIN = 01401,
OBJC_ASSOCIATION_COPY = 01403
};
简单的来说,就是你添加的属性是用什么修饰的,就选择对应的策略。
枚举值 | 对应属性 | 说明 |
---|---|---|
OBJC_ASSOCIATION_ASSIGN | @property (assign) or @property (unsafe_unretained) | 弱引用关联对象 |
OBJC_ASSOCIATION_RETAIN_NONATOMIC | @property (strong, nonatomic) | 强引用关联对象,且为非原子操作 |
OBJC_ASSOCIATION_COPY_NONATOMIC | @property (copy, nonatomic) | 复制关联对象,且为非原子操作 |
OBJC_ASSOCIATION_RETAIN | @property (strong, atomic) | 强引用关联对象,且为原子操作 |
OBJC_ASSOCIATION_COPY | @property (copy, atomic) | 复制关联对象,且为原子操作 |