CallBack-回調,在各個編程語言中都是很重要的一個功能,比如彈窗一個子控件,在子控件中獲取到信息後需要返回數據給調用方,最主流的做法就是把調用方作爲參數傳到子控件中,然後當子控件拿到信息以後再把信息傳遞給之前進來的調用方。在iOS中,是通過一種叫delegate(代理)的方式來實現,通常在創建子控件的時候會看到一句:子控件.delegate = self, 意思是說,我就是這個子控件的代理,當子控件需要回調的時候,就直接調用其成員delegate(也就是我), 就可以把信息傳遞回去。
然而在很多情況下這樣的代理會顯得很‘囉嗦’,比如我只是想簡單的彈出一個對話框(UIAlertView)然後知道用戶選擇的是Ok還是Cancel, 卻要在調用方做兩件事,首先是聲明這個對話框的協議(protocol)用以具備接收對話框回調的資格,然後再去實現回調方法,比如OnOk, OnCancel,等等,更麻煩的是,如果我在此有三個對話框的話,就要實現多個協議,做他們的代理。
所以這裏可以用Block對其閉包,直接在上下文的地方聲明回調,不僅不用增加回調方法,而且很輕易的解決了多重回調的複雜局面。現在就拿UIAlertView舉一個例子:
正常做法:調用方在召喚alertview的同時,將其代理指向自己:alertview.delegate = self; 然後在用戶點擊對話框上面的按鈕的時候,觸發其回調方法(如果它的代理存在的前提,如果沒有聲明指向的話不會被調用):
// Called when a button is clicked. The view will be automatically dismissed after this call returns
- (void)alertView:(UIAlertView*)alertView clickedButtonAtIndex:(NSInteger)buttonIndex;
這裏的優化思路是:
修改UIAlertView這個類,增加一個block對象,然後讓其自己做自己的delegate, 調用方將block作爲參數傳進去以後,在自己的clickedButtonAtIndex方法中調用block即可。
具體實現:
1. 創建UIAlertView的類擴展,UIAlertView+Block;
2. 聲明回調Block:
typedefvoid (^UIAlertViewCompletionBlock) (UIAlertView *alertView,NSIntegerbuttonIndex);
3. 增加接口,將block傳進來.
+ (instancetype)showWithTitle:(NSString *)title
message:(NSString *)message
style:(UIAlertViewStyle)style
cancelButtonTitle:(NSString *)cancelButtonTitle
otherButtonTitles:(NSArray *)otherButtonTitles
tapBlock:(UIAlertViewCompletionBlock)tapBlock;
4. 在設置tapBlock的時候,修改對象,新增成員變量,此處是核心:
objc_setAssociatedObject(self,UIAlertViewTapBlockKey, tapBlock,OBJC_ASSOCIATION_COPY);
*UIAlertViewTapBlockKey 不是字符串,是一個void*, 與tapBlock形成K-V唯一配對,向一個UIAlertView查key,就可以得到tapBlock作爲value返回。 這裏的setAssociatedObject是運行時編程中API,所以需要#import<objc/runtime.h>
此時,tapBlock就已被植入到對象UIAlertView中,同時可以用key-value的方式獲取。
5.將代理指向自己
self.delegate = (id<UIAlertViewDelegate>)self;
6.實現代理方法- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex,在裏面調用植入的tabBlock.