动态语言函数
OC是一种动态语言,它的方法,对象的类型都是到运行的时候才可以肯定的。因此这就使得OC存在了关联对象这一强大的机制。atom
关联对象spa
所谓关联对象,其实就是咱们在运行时对一个已存在的对象上面绑定一个对象,使两个对象变成动态的聚合关系。指针
关联对象和属性同样有着关键字,如下是关联对象的存储策略:code
关联类型 | 等效的@property属性 |
OBJC_ASSOCIATION_ASSIGN | assign |
OBJC_ASSOCIATION_RETAIN_NONATOMIC | nonatomic,retain |
OBJC_ASSOCIATION_COPY_NONATOMIC | nonatomic,copy |
OBJC_ASSOCIATION_RETAIN | retain |
OBJC_ASSOCIATION_COPY | copy |
关联对象主要靠下面三个函数,它们都位于<objc/runtime>下对象
void objc_setAssociatedObject(id object, void *key ,id value ,objc_AssociationPolicy policy)blog
设置关联对象内存
参数 | 说明 |
object | 要进行关联的对象 |
key | 一个内存表示,在比较两个关联对象是否相等时,比较的就是内存地址,因此通常用一个全局静态变量表示 |
value | 被关联的对象 |
policy | 存储策略 |
id objc_getAssociatedObject(id object, void *key)ci
获取关联对象rem
void objc_removeAssociatedObjects(id object)
删除关联对象
做用
关联对象通常用于动态的扩展一个对象,可是这通常都是在其余方法不行的过后才会使用,由于关联对象极可能会出现难以查找的bug。
关联对象有时也会用于在category向类添加属性,这点等会儿在分析。
下面Demo在UIAlertView上面动态绑定了一个block,把按钮处理逻辑和alert调用整合在了一块儿。
static char *AlertKey = "alertKey"; - (void)viewDidLoad { [super viewDidLoad]; alert = [[UIAlertView alloc] initWithTitle:@"alert" message:@"message" delegate:self cancelButtonTitle:@"cancel" otherButtonTitles:@"other", nil]; } - (IBAction)click:(id)sender { void (^block) (NSInteger) = ^(NSInteger buttonIndex){ if (buttonIndex == 0){ NSLog(@"click cancel"); } else{ NSLog(@"click other"); } }; objc_setAssociatedObject(alert, AlertKey, block, OBJC_ASSOCIATION_COPY); [alert show]; } - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { void (^block)(NSInteger) = objc_getAssociatedObject(alertView, AlertKey); block(buttonIndex); }
运行程序点击两个按钮分别输出以下
2015-08-11 22:51:27.146 Dynamic[4592:2833778] click other 2015-08-11 22:51:28.262 Dynamic[4592:2833778] click cancel
接下来咱们给UIViewController在category中添加一个addition属性
#import "ViewController.h" #import <objc/runtime.h> @interface UIViewController (Addition) @property(nonatomic ,copy) NSString *addition; @end
#import "UIViewController+Addition.h" static const void *IndieBandNameKey = &IndieBandNameKey; @implementation UIViewController (Addition) -(void)setAddition:(NSString *)addition{ objc_setAssociatedObject(self, IndieBandNameKey, addition, OBJC_ASSOCIATION_COPY); } -(NSString *)addition{ return objc_getAssociatedObject(self, IndieBandNameKey); } @end
这里说明一下,这里关联的其实是形参addition,和属性没有什么关系,写@property 就是为了可以使用‘.’语法。可是@porperty里面的属性存储策略仍是要和关联对象一致的,这样不容易形成误解。
因此每次setAddition实际上咱们并非修改了原有内存的值,而是改变了指针指向的地址,这里须要注意一下。
而后修改刚才alert的代码
- (void)viewDidLoad { [super viewDidLoad]; alert = [[UIAlertView alloc] initWithTitle:@"alert" message:@"message" delegate:self cancelButtonTitle:@"cancel" otherButtonTitles:@"other", nil]; alert.delegate = self; self.addition = @"addition"; } - (IBAction)click:(id)sender { void (^block) (NSInteger) = ^(NSInteger buttonIndex){ if (buttonIndex == 0){ NSLog(@"click cancel"); objc_removeAssociatedObjects(self); } else{ NSLog(@"click other"); NSLog(@"%@",self.addition); } }; objc_setAssociatedObject(alert, AlertKey, block, OBJC_ASSOCIATION_COPY); [alert show]; } - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { void (^block)(NSInteger) = objc_getAssociatedObject(alertView, AlertKey); block(buttonIndex); }
注意三条加粗的语句,而后咱们运行看结果
2015-08-11 22:53:54.353 Dynamic[4655:2849502] click other 2015-08-11 22:53:54.353 Dynamic[4655:2849502] addition 2015-08-11 22:53:55.804 Dynamic[4655:2849502] click cancel 2015-08-11 22:53:57.491 Dynamic[4655:2849502] click other 2015-08-11 22:53:57.491 Dynamic[4655:2849502] (null)
首先咱们使用了关联对象,因此点击other的时候会看到打印出了addition。点击cancel的时候又由于咱们remove了关联对象,此时再点击other的时候addition就变成null了。