iOS 关联对象
objc_setAssociatedObject 关联对象
使用关联,我们可以不用修改类的定义而为其对象增加存储空间,这在我们无法访问类的源码的时候或者是考虑到二进制兼容性的时候是非常有用的.
和类别category一样,可以通过关联对象给二个对象之间增加联系.
但是category只能增加方法,不能增加成员变量,这时可以使用objc_setAssociatedObject和objc_getAssociatedObjct方法为其增加成员变量
因为是运行时的特性,所以需要倒入
#import <objc/runtime.h>
主要函数:
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);
基本说明:
objc_setAssociatedObject 相当于 setValue: forkey 进行关联 value对象
objc_getAssociatedObject 用来读取对象
objc_AssociationPolicy 属性 是设定该value在object内的属性,即assign(ratain, nonatomic)... 等
objc_removeAssociatedObjects 函数来移除一个关联对象,或者使用objc_setAssociatedObject函数将key指定的关联对象设置为nil
相关参数:
key: 要保证全局唯一,key与关联的对象是一一对象关系,必须全局唯一通常用@selector(methodName)作为key.
value: 要关联的对象
policy: 关联策略, 有五种关联策略
OBJC_ASSOCIATION_ASSIGN 等价于 @property(assign)
OBJC_ASSOCIATION_RETAIN_NONATOMIC 等价于 @propery(strong, nonatomic)
OBJC_ASSOCIATION_COPY_NONATOMIC 等价于 @propery(copy, nonatomic)
OBJC_ASSOCIATION_RETAIN 等价于 @propery(strong, atomic)
OBJC_ASSOCIATION_COPY 等价于 @propery (copy, atomic)
使用场景:
关联对象相当于实例变量,在类别(也有人叫分类)里面, 不能创建实例变量,关联对象就可以解决这种问题(对应属性,有对应属性的runtime解决方法)
关联对象参数key,一共有三种写法:
- 静态变量 &btnkey
- @selector(methodName)
- _cmd 相当于@selector(methodName) 但作用域只在当前方法里
Demo: 新建一个UIButton的类别
1. 导入头文件
#import <objc/runtime.h>
2.新建一个Action的Category
在.h中添加一个事件的Block,代码如下:
#import <UIKit/UIKit.h>
typedef void (^ActionBlock)(UIButton *button);
@interface UIButton(Action)
@property (nonatomic, copy) ActionBlock actionBlock;
+(UIButton *)createBtnWithFrame:(CGRect)frame title:(NSString *)title actionBlock:(ActionBlock)actionBlock;
在这里我们创建了一个加方法(类方法),来创建一个按钮:
实现如下:
+(UIButton *)createBtnWithFrame:(CGRect)frame title:(NSString *)title actionBlock:(ActionBlock)actionBlock{
UIButton *button = [[UIButton alloc] init];
button.frame = frame;
[button setTitle: title forState: UIControlStateNormal];
[button addTarget: button action:@selector(buttonClick:) forControlEvents: UIControlEventTouchUpInside];
objc_setAssociatedObject(button, &keyOfMethod, actionBlock, OBJC_ASSOCIATION_COPY_NONATOMIC);
return button;
}
注意这里声明了两个索引KEY
static NSString * keyOfMethod;
static NSString * keyOfBlock;
-(void)buttonClick:(UIButton *)button{
//通过key获取被关联对象
//objc_getAssociatedOject(id object, const void *key);
ActionBlock block1 = (ActionBlock)objc_getAssociatedObject(button, &keyOfMethod);
if (block1){
block1(button);
}
ActionBlock block2 = (ActionBlock)objc_getAssociatedObject(button, &keyOfBlock);
if (block2){
block2(button);
}
}
-(void)setActionBlock:(ActionBlock)actionBlock{
objc_setAssociatedObject(self, &keyOfBlock, actionBlcok, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
-(ActionBlock)actionBlock{
return objc_associatedObject(self, _cmd); // 等价于 &keyOfBlock
}
获取apple私有api(通过runtime获取apple私有的方法)
-(void)getiOSPrivateAPI{
NSString *className = NSStringFromClass([UIView class]);
const char *cClassName = [className UTF8String];
id theClass = objc_getClass(cClassName);
unsigned int outCount;
Method *m = class_copyMethodList(theClass, &outCount);
NSLog(@"%d", outCount);
for (int i = 0; i<outCount; i++){
SEL a = method_getName(*(m+i));
NSString *sn = NSStringFromSelector(a);
NSLog(@"%@", sn);
}
}