1、详解:associative
objective-c有两个扩展机制:category和associative。我们可以通过category对已经存在的类添加和扩展方法,但是它有一个很大的局限性,那就是不能扩展属性。于是,就有了专门用来扩展属性的机制:associative。关联对象是Runtime中一个非常实用的特性。
associative的主要原理,就是在运行时把两个对象相互关联起来,使得其中的一个对象(A)作为另外一个对象(B)的一部分。即A对象通过给定的key连接到B对象上,作为B对象的一个属性。使用associative,我们可以不用修改类的定义而为其对象增加存储空间,同时也可以保证被关联的对象在关联对象的整个生命周期都是可用的。
associative机制提供了三个方法:
OBJC_EXPORT void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_1);
OBJC_EXPORT id objc_getAssociatedObject(id object, const void *key)
__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_1);
OBJC_EXPORT void objc_removeAssociatedObjects(id object)
__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_1);
objc_getAssociatedObject 函数可以动态的为已经存在的类关联一个对象(添加属性),同时objc_setAssociatedObject可以获得添加的关联对象(获得属性),objc_removeAssociatedObjects 可以断开添加的关联对象(移除属性)。object参数作为待扩展的源对象实例(需要增加属性的对象),key作为该对象实例将要增加的属性的键,key是一个void指针(constvoid *)。而value就是将要增加的属性的值(关联的对象),policy作为关联的策略。关联策略表明了相关的对象是通过赋值,保留引用还是复制的方式进行关联的,它指定了关联对象的内存管理策略。
关联策略的枚举包括:
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. */
};
特别需要注意的是,当宿主对象被释放时,会根据指定的内存管理策略来处理关联对象。如果指定的策略是assign ,则宿主释放时,关联的对象不会被释放;而如果指定的是retain或者是copy,则宿主释放时,关联的对象会被释放。
2、使用:动态的为NSObject类添加一个myProperty属性
首先创建NSObject的一个category类别,MyObject类。导入头文件<objc/runtime.h>。在头文件为MyObject类添加一个属性myProperty。
//
// NSObject+MyObject.h
// JBAlertView
//
// Created by jaybin on 15/6/16.
// Copyright (c) 2015年 jaybin. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSObject (MyObject)
@property (nonatomic, retain) NSString *myProperty;//为NSObject动态增加的属性
@end
//
// NSObject+MyObject.m
// JBAlertView
//
// Created by jaybin on 15/6/16.
// Copyright (c) 2015年 jaybin. All rights reserved.
//
#import "NSObject+MyObject.h"
#import <objc/runtime.h>
@implementation NSObject (MyObject)
/**
* 获取关联对象(获取属性)
*/
-(NSString *)myProperty{
return objc_getAssociatedObject(self, @selector(myProperty));
}
/**
* 设置关联对象(添加属性)
*/
-(void)setMyProperty:(NSString *)myProperty{
objc_setAssociatedObject(self, @selector(myProperty), myProperty, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
到这里,我们就通过Runtime特性为NSObject动态添加了一个myProperty属性。
3、为UIAlertView动态添加一个Block属性,将AlertView与button的点击操作关联,简单的区分处理一个视图有多个AlertView弹窗。
我们经常会遇到一个视图里有多个AlertView弹窗的情况。这个时候,如果按照常规的做法,我们就会定义一个全局的变量来去区分到底现在弹出或者点击的是哪个弹窗。然后在AlertView的代理方法
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex;
里面使用 if else 多个语句块来区分多个弹窗点击后的操作,反正用起来很麻烦。如果我们将每个AlertView与它们的button点击操作关联起来,那么,就不需要再在代理方法里面用多个语句块来区分执行每个弹窗按钮的响应事件了,将AlertView的声明初始化和按钮的响应事件代码放在一起,代码的清晰度和维护也会更好一点。所以,我们可以考虑为UIAlertView动态添加一个Block属性,将AlertView与button的点击操作关联。
首先,我们创建UIAlertView的一个category类别,JBAlertView类。在头文件为JBAlertView类添加一个属性 alertViewBlock。
//
// UIAlertView+JBAlertView.h
// JBAlertView
//
// Created by jaybin on 15/4/25.
// Copyright (c) 2015年 jaybin. All rights reserved.
//
#import <UIKit/UIKit.h>
/**
* JBAlertViewBlock
* cancel: index = 0
* ok: index = 1
*/
typedef void (^JBAlertViewBlock)(NSInteger index);
@interface UIAlertView (JBAlertView)
@property (nonatomic, copy) JBAlertViewBlock alertViewBlock;//为UIAlertView动态增加的Block属性
@end
//
// UIAlertView+JBAlertView.m
// JBAlertView
//
// Created by jaybin on 15/4/25.
// Copyright (c) 2015年 jaybin. All rights reserved.
//
#import "UIAlertView+JBAlertView.h"
#import <objc/runtime.h>
@implementation UIAlertView (JBAlertView)
/**
* 获取关联对象(获取属性)
*/
-(JBAlertViewBlock)alertViewBlock{
return objc_getAssociatedObject(self, @selector(alertViewBlock));
}
/**
* 设置关联对象(添加属性)
*/
-(void)setAlertViewBlock:(JBAlertViewBlock)block{
objc_setAssociatedObject(self, @selector(alertViewBlock), block, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
到这里,我们就 为UIAlertView动态添加了一个alertViewBlock属性。
最后我们的使用就会非常方便了。假设页面有两个AlertView弹窗,那么我们的代码就会比以前简洁清晰了。
#import "ViewController.h"
#import "UIAlertView+JBAlertView.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIButton *btn1 = [UIButton buttonWithType:UIButtonTypeCustom];
[btn1 setBackgroundColor:[UIColor lightGrayColor]];
[btn1 setTitle:@"show alert1" forState:UIControlStateNormal];
btn1.frame = CGRectMake(150, 100, 100, 50);
[btn1 addTarget:self action:@selector(showAlertView1) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:btn1];
UIButton *btn2 = [UIButton buttonWithType:UIButtonTypeCustom];
[btn2 setTitle:@"show alert2" forState:UIControlStateNormal];
[btn2 setBackgroundColor:[UIColor lightGrayColor]];
btn2.frame = CGRectMake(150, 200, 100, 50);
[btn2 addTarget:self action:@selector(showAlertView2) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:btn2];
}
//初始化AlertView1 AlertView的初始化和按钮的响应事件代码放在一起
- (void)showAlertView1{
//AlertView的初始化
UIAlertView *alertView1 = [[UIAlertView alloc] initWithTitle:@"alertView1" message:nil delegate:self cancelButtonTitle:@"cancel" otherButtonTitles:@"ok", nil];
//按钮的响应事件处理
alertView1.alertViewBlock = ^(NSInteger index){
if(1==index){
NSLog(@"click OK_button");
}
else if (0==index){
NSLog(@"click Cancel_button");
}
};
[alertView1 show];
}
//初始化AlertView2 AlertView的初始化和按钮的响应事件代码放在一起
- (void)showAlertView2{
//AlertView的初始化
UIAlertView *alertView2 = [[UIAlertView alloc] initWithTitle:@"alertView2" message:nil delegate:self cancelButtonTitle:@"cancel" otherButtonTitles:@"ok", nil];
//按钮的响应事件处理
alertView2.alertViewBlock = ^(NSInteger index){
if(1==index){
NSLog(@"click OK_button");
}
else if (0==index){
NSLog(@"click Cancel_button");
}
};
[alertView2 show];
}
//实现UIAlertViewDelegate方法
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
alertView.alertViewBlock(buttonIndex);
}
最后记得要在AlertView的代理方法里回调为AlertView动态添加的属性Block,并且传入点击的buttonIndex。
项目的代码已放上GitHub。https://github.com/jaybinhe/JBAlertView