虽然很早很早就知道有这么个牛逼的存在。看过一些皮毛,看完之后哦了几声感觉高大上,仿佛看懂了。不过过了一会就不知道刚才看的是什么了!
看了看MJRefresh和YYModel源码中,看到了这个方法,使用频率挺高。记录一下,多一些认识
1、 关联对象方法
objc_setAssociatedObject / objc_getAssociatedObject
一看名字就知道,类似get和set方法。一个赋值一个取值。
来把一个对象与另外一个对象进行关联。该函数需要四个参数:源对象,关键字,关联的对象和一个关联策略。
OBJC_EXPORT void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
1.OBJC_EXPORT 打包lib时,用来说明该函数是暴露给外界调用的。
2.id object 表示关联者,是一个对象,变量名理所当然也是object
3.id value 表示被关联者,变量名是value,它要关联到object上的。
4.关键字是一个void类型的指针。每一个关联的关键字必须是唯一的。通常都是会采用静态变量来作为关键字。
5.关联策略表明了相关的对象是通过赋值,保留引用还是复制的方式进行关联的;还有这种关联是原子的还是非原子的。这里的关联策略和声明属性时的很类似。这种关联策略是通过使用预先定义好的常量来表示的。
关联策略我感觉我和声明属性一样,retain和copy也是对应的。
enum { OBJC_ASSOCIATION_ASSIGN = 0,
OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1,
OBJC_ASSOCIATION_COPY_NONATOMIC = 3,
OBJC_ASSOCIATION_RETAIN = 01401,
OBJC_ASSOCIATION_COPY = 01403};
6.断开关联
断开关联是使用objc_setAssociatedObject函数,传入nil值即可。
使用函数objc_removeAssociatedObjects可以断开所有关联。
一个简单例子,给一个数组添加一个属性。
#import "ViewController.h"
#import <objc/runtime.h>
//关联对象的静态变量的关键字
static const NSString *associationKey = @"associationKey";
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSArray *arr = @[@"11", @"22", @"33"];
NSString *str = @"44";
//添加一个属性对象
objc_setAssociatedObject(arr, &associationKey, str, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
NSString *getStr = objc_getAssociatedObject(arr, &associationKey);
NSLog(@"666_SunDePrint_999:%@\n%@", getStr, arr);
}
@end
再看一个例子
需求:自定义了分享面板,点击分享QQ或者微信后,我需要再当前页面获得分享的结果,成功或者失败。
由于是自定义view,QQ微信的图标按钮就需要自己写个button,分享结果来自微信分享SDK的回调。所以初始化自定义分享面板方法的时候,需要写个block作为参数,传递成功和失败的结果。(描述的不清楚)
由于每个按钮是没关系的,所以点击各个分享按钮后,得到各自对应的结果,需要回调到各自按钮的点击事件里。
所以就需要每个按钮有个block属性,才能在点击事件里调用控制器中的block做想做的事。
1、自定义的view,作为分享view
上面一个按钮,点击后就调用分享SDK,获得结果传递给控制器。
h
#import <UIKit/UIKit.h>
@interface ShareView : UIView
- (ShareView *)initWithFame:(CGRect)frame Result:(void (^)(BOOL result))block;
@end
m
#import "ShareView.h"
#import <objc/runtime.h>
static NSString *share_weixin_key = @"share_weixin_key";
@implementation ShareView
- (ShareView *)initWithFame:(CGRect)frame Result:(void (^)(BOOL result))block {
self = [super initWithFrame:frame];
if (self) {
UIButton *weixinBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, frame.size.width/2, frame.size.height/2)];
[weixinBtn setBackgroundImage:[UIImage imageNamed:@"111"] forState:0];
[weixinBtn addTarget:self action:@selector(weixinClick:) forControlEvents:64];
//添加一个属性对象
objc_setAssociatedObject(weixinBtn, &share_weixin_key, block, OBJC_ASSOCIATION_COPY_NONATOMIC);
[self addSubview:weixinBtn];
}
return self;
}
- (void)weixinClick:(UIButton *)sender {
//根据key,取出添加的属性
void (^block)(BOOL result) = objc_getAssociatedObject(sender, &share_weixin_key);
sleep(2);//模拟分享消耗2s
//2s后,得到结果成功或者失败
if (block) {
block(YES);
}
}
2、在控制器中的调用
ShareView *share = [[ShareView alloc] initWithFame:CGRectMake(0, 100, self.view.frame.size.width, 200) Result:^(BOOL result){
if (result == YES) {
NSLog(@"666_SunDePrint_999:%@", @"分享成功");
}else{
NSLog(@"666_SunDePrint_999:%@", @"分享成功");
}
}];
share.backgroundColor = [UIColor greenColor];
[self.view addSubview:share];
这算是runtime关联对象方法的一个应用了。当然可以用别的方法传递结果啥的,我自定义的分享面板view好像有些问题。哈哈。
3、给category添加属性
一般用category都是用来添加方法用的,不能添加属性,但是有时候确实需要额外一些属性,就可以用关联方法来添加属性了。MJRefresh等三方库中经常用到。
2、获取对象所有属性方法
class_copyPropertyList 、class_copyIvarList
有2个方法用法一样,前者是获取属性方法,后者还可以额外获取成员变量。最后记得释放数组。
参数传入当前类和一个数值,获取属性后,可以给这个数值赋值,得到属性的数量。返回一个属性数组。
class_copyPropertyList 相关方法
// 获取所有属性
class_copyPropertyList
说明:使用class_copyPropertyList并不会获取无@property声明的成员变量
// 获取属性名
property_getName
// 获取属性特性描述字符串
property_getAttributes
// 获取所有属性特性
property_copyAttributeList
class_copyIvarList相关方法
// 获取所有成员变量
class_copyIvarList
// 获取成员变量名
ivar_getName
// 获取成员变量类型编码
ivar_getTypeEncoding
// 获取指定名称的成员变量
class_getInstanceVariable
// 获取某个对象成员变量的值
object_getIvar
// 设置某个对象成员变量的值
object_setIvar
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
Class selfCla = [self class];
unsigned int proCunt;
objc_property_t *pros = class_copyPropertyList(selfCla, &proCunt);
NSLog(@"666_SunDePrint_999:%d", proCunt);
for (int i = 0; i < proCunt; i++) {
objc_property_t pro = pros[i];
NSString *proName = [[NSString alloc] initWithCString:property_getName(pro) encoding:NSUTF8StringEncoding];
NSLog(@"666_SunDePrint_999:%@", proName);
}
free(pros);
unsigned int varCount;
Ivar *porList = class_copyIvarList(selfCla, &varCount);
NSLog(@"666_SunDePrint_999:%d", varCount);
for (int i = 0; i < varCount; i++) {
Ivar list = porList[i];
NSString *name = [[NSString alloc] initWithCString:ivar_getName(list) encoding:NSUTF8StringEncoding];
NSLog(@"666_SunDePrint_999--var:%@", name);
}
free(porList);
}
3、获取有关属性信息的结构体
property_getAttributes函数返回objc_property_attribute_t结构体列表,objc_property_attribute_t结构体包含name和value,常用的属性如下:
属性类型 name值:T value:变化
编码类型 name值:C(copy) &(strong) W(weak)空(assign) 等 value:无
非/原子性 name值:空(atomic) N(Nonatomic) value:无
变量名称 name值:V value:变化
使用property_getAttributes获得的描述是property_copyAttributeList能获取到的所有的name和value的总体描述,如 T@"NSDictionary",C,N,V_dict1
for (int i = 0; i < proCunt; i++) {
objc_property_t pro = pros[i];
NSString *proName = [[NSString alloc] initWithCString:property_getName(pro) encoding:NSUTF8StringEncoding];
const char *attrs = property_getAttributes(pro);
NSString *propertyAttributes = @(attrs);
NSArray *attributeItems = [propertyAttributes componentsSeparatedByString:@","];
NSLog(@"666_SunDePrint_999:%@--%@", proName, attributeItems);
}
网友评论