Runtime应用(二)使用对象关联为分类增加属性(每个对象的属性互不干扰)
一、对象的关联方法有
1、 void objc_setAssociatedObject(id object, const void *key, id value,objc_AssociationPolicy policy) ,关联对象(将值value与对象object关联起来)
参数key:将来可以通过key取出这个存储的值
参数policy:存储策略(assign、copy、retain)
2、 id objc_getAssociatedObject(id object, const void *key) ,利用参数key将对象中存储的对应值取出
二、关联的作用一,给分类添加属性(下面为每个对象添加属性)(可作为对象的标签或存储信息)
大家都知道分类的作用是
/* 分类的作用:在不改变原来类内容的基础上,可以为类增加一些方法 使用注意: 1.分类只能增加方法,不能增加成员变量(其实不然啊) 2.分类方法实现中可以访问原来类中声明的成员变量 3.分类可以重新实现原来类中的方法,但是会覆盖掉原来的方法,会导致原来的方法没法再使用 4.方法调用的优先级:分类(最后参与编译的分类优先) --> 原来类 --> 父类 */
我们知道OC比较爽的就是可以添加分类,在分类中添加自己喜欢的方法。可能有人会问了,不能添加属性啊 ,其实不然。用objc_setAssociatedObject(关联)就可以给分类添加属性了
声明代码:
@interface NSObject (CX) /** * 为每一个对象添加一个name属性 */ @property (nonatomic,copy) NSString *name; /** * 为每个对象添加一个数组属性 */ @property (nonatomic,strong) NSArray *books; @end
实现代码:
// 用一个字节来存储key值,设置为静态私有变量,避免外界修改 static char nameKey; - (void)setName:(NSString *)name { // 将某个值与某个对象关联起来,将某个值存储到某个对象中 objc_setAssociatedObject(self, &nameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC); } - (NSString *)name { return objc_getAssociatedObject(self, &nameKey); } static char booksKey; - (void)setBooks:(NSArray *)books { objc_setAssociatedObject(self, &booksKey, books, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (NSArray *)books { return objc_getAssociatedObject(self, &booksKey); }
测试:
NSString *str = @"xx"; str.name = @"oo"; str.books = @[@"xxoo",@"ooxx"]; NSLog(@"%@,%@",str.name,str.books);
打印如下
2016-08-23 12:18:37.642 RunTimeTest[1801:95084] oo,( xxoo, ooxx ) (lldb)
这样连字符串页具备了一个数组属性。
三、对象关联的另一种作用:在既有类中使用关联对象存放自定义数据
在ios开发时经常会用到UIAlertView类,该类提供了一种视图向用户展示警告信息。该类通过代理协议来处理用户的点击事件,但由于使用了代理就必须把创建警告的视图和处理按钮动作的代码分开,比如说
- (void)showAlertView { UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"UIAlertView" message:@"what do you do" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"sure", nil]; [alert show]; } - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { if (0 == buttonIndex) { NSLog(@"%@",@"cancel"); } else if (1 == buttonIndex) { NSLog(@"%@",@"sure"); } }
如果想在同一个类里处理多个警告信息视图,我们必须在代理方法中比较点击按钮的索引,并借此编写相应的逻辑,如果能够在创建警告视图的时候直接把处理逻辑的代码写好那将会很便于阅读,这时可以考虑用关联对象,设定一个与alert对象关联的代码块,等到执行代理方法时再将其读取出来
- (void)viewDidLoad { [super viewDidLoad]; [self showAlertView]; } static char myAlerViewKey; - (void)showAlertView { UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"UIAlertView" message:@"what do you do" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"sure", nil]; // 将逻辑定义到代码块里面 void(^block)(NSInteger) = ^(NSInteger buttonIndex) { if (buttonIndex == 0) { NSLog(@"%ld",buttonIndex); } else { NSLog(@"%ld",buttonIndex); } }; // 对象关联 objc_setAssociatedObject(alert, &myAlerViewKey, block, OBJC_ASSOCIATION_COPY); [alert show]; } - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { void(^block)(NSInteger) = objc_getAssociatedObject(alertView, &myAlerViewKey); block(buttonIndex); }
每个对象的属性互不干扰
说到这里了在补充一下 其实还可以利用关联来传递数据如
- (void)viewDidLoad { [super viewDidLoad]; // static const char associatedButtonkey UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom]; [btn setTitle:@"点我" forState:UIControlStateNormal]; [self.view addSubview:btn]; [btn setFrame:CGRectMake(50, 50, 50, 50)]; btn.backgroundColor = [UIColor redColor]; [btn addTarget:self action:@selector(click:) forControlEvents:UIControlEventTouchUpInside]; } -(void)click:(UIButton *)sender { NSString *message = @"你是谁"; UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"提示" message:@"我要传值·" delegate:self cancelButtonTitle:@"确定" otherButtonTitles:nil]; alert.delegate = self; [alert show]; //#import <objc/runtime.h>头文件 //objc_setAssociatedObject需要四个参数:源对象,关键字,关联的对象和一个关联策略。 //1 源对象alert //2 关键字 唯一静态变量key associatedkey //3 关联的对象 sender //4 关键策略 OBJC_ASSOCIATION_ASSIGN // enum { // 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. */ // }; //把alert和message字符串关联起来,作为alertview的一部分,关键词就是msgstr,之后可以使用objc_getAssociatedObject从alertview中获取到所关联的对象,便可以访问message或者btn了
//即实现了关联传值 objc_setAssociatedObject(alert, @"msgstr", message,OBJC_ASSOCIATION_ASSIGN); objc_setAssociatedObject(alert, @"btn property",sender,OBJC_ASSOCIATION_ASSIGN); }
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { //通过 objc_getAssociatedObject获取关联对象 NSString *messageString =objc_getAssociatedObject(alertView, @"msgstr"); UIButton *sender = objc_getAssociatedObject(alertView, @"btn property"); NSLog(@"%ld",buttonIndex); NSLog(@"%@",messageString); NSLog(@"%@",[[sender titleLabel] text]); //使用函数objc_removeAssociatedObjects可以断开所有关联。通常情况下不建议使用这个函数,因为他会断开所有关联。只有在需要把对象恢复到“原始状态”的时候才会使用这个函数。 } 终端打印: 2015-07-22 16:18:35.294 test[5174:144121] 0 2015-07-22 16:18:35.295 test[5174:144121] 你是谁 2015-07-22 16:18:35.295 test[5174:144121] 点我