关于什么是关联引用,这两篇文章作了详细的解释 Objective-C – 关联引用 Objective-C Associative References
熟悉Category都知道,我们是不能在Category能创建实例变量的,但我们可以通过Associative References来达到目的。关联引用说白点,就是你可以往一个已存在的类添加属性,可以设置KEY,VALUE,当然你需要,也可以设置多个。通过上面的文章相信你已经知道什么是关联引用,但到底能干点什么,相信与我刚了解的时候疑惑是一样的。下面举例说明。
下面的UIAlertView block回调是通过Category + 关联引用来实现的。源码截取于:BlockUI
//UIAlertView
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"title"
message:@"message"
delegate:self
cancelButtonTitle:@"ok"
otherButtonTitles:nil];
[alert showWithCompletionHandler:^(NSInteger buttonIndex) {
NSLog(@"button:%d",buttonIndex);
}];
上面的代码的回调相当的简洁,实用性很强。你只要往里边塞你的代码就可以了。省掉了写delegate的方法。
#import <UIKit/UIKit.h>
#import <objc/runtime.h>
@interface UIView(BUIView)<UIAlertViewDelegate>
//UIAlertView
-(void)showWithCompletionHandler:(void (^)(NSInteger buttonIndex))completionHandler;
@end
#import "BUIView.h"
#import <objc/runtime.h>
@implementation UIView(BUIView)
const char oldDelegateKey;
const char completionHandlerKey;
#pragma -mark UIAlerView
-(void)showWithCompletionHandler:(void (^)(NSInteger buttonIndex))completionHandler
{
UIAlertView *alert = (UIAlertView *)self;
// CcompletionHandler 是一个Block变量,也就是我们在{}写的代码
if(completionHandler != nil)
{
// 检索关联对象
id oldDelegate = objc_getAssociatedObject(self, &oldDelegateKey);
// 因为可能多次调用同一对象,所以先判断是否已设置。
if(oldDelegate == nil)
{
// 往UIAlerView 塞进了一个 变Key:oldDelegateKey 变量为:oldDelegate
// 按我理解,完全可以当作是实例变量,其实也就是本Delegate。当你发现,为什么要赋一个nil值,其实作者在下边有赋值
objc_setAssociatedObject(self, &oldDelegateKey, oldDelegate, OBJC_ASSOCIATION_ASSIGN);
}
// 设置 Delegate 与 关联引用
oldDelegate = alert.delegate;
alert.delegate = self;
// 向 往UIAlerView 添加 completionHandlerKey 和 completionHandler
// 目的是为了在 UIAlerView Delegate - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex 中使用
objc_setAssociatedObject(self, &completionHandlerKey, completionHandler, OBJC_ASSOCIATION_COPY);
}
// 弹出 往UIAlerView
[alert show];
}
// UIAlerView Delegate
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
UIAlertView *alert = (UIAlertView *)self;
// 检索关联对象, CcompletionHandler 也就是我们写的{}中的代码
void (^theCompletionHandler)(NSInteger buttonIndex) = objc_getAssociatedObject(self, &completionHandlerKey);
if(theCompletionHandler == nil)
return;
// 执行
theCompletionHandler(buttonIndex);
// 检索关联对象 oldDelegateKey
alert.delegate = objc_getAssociatedObject(self, &oldDelegateKey);
}
我对源码加上了注释,希望你能更快的了解其作用和理解关联对象的使用方法。
上面的代码执行顺序是t先执行
-(void)showWithCompletionHandler:(void (^)(NSInteger buttonIndex))completionHandler方法
当点击按扭的时候,就执行
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
最后执行我们写的代码
[alert showWithCompletionHandler:^(NSInteger buttonIndex) {
NSLog(@"button:%d",buttonIndex);
}];
这方法很清淅,很简单的实现了UIAlerView Block的回调方法。基于这种思路,你可以自己实现很多的回调的也可以参考或者使用BlockUI的源码。