- 說明:通過一個(gè)廢棄的alertView,詳解運(yùn)行時(shí)關(guān)聯(lián)的一個(gè)巧妙的實(shí)例
基礎(chǔ)知識:
- 用runtime需要導(dǎo)入頭文件
#import "objc/runtime.h"
- 方法調(diào)用的本質(zhì)娇斑,就是讓對象發(fā)送消息
- objc_msgSend,只有對象才能發(fā)送消息,因此以objc開頭
- 給一個(gè)分類添加屬性溉瓶,其實(shí)本質(zhì)就是給這個(gè)分類添加關(guān)聯(lián)照皆,并不是直接把這個(gè)值的內(nèi)存空間添加到類存空間
- 運(yùn)行時(shí)參數(shù)配置PROJECT ->Build Settings ->搜索msg ->Enable Strict Checking of objc_msgSend Calls ->更改為No
應(yīng)用場景
- 如果一個(gè)頁面調(diào)用了多次alertView彈窗,根據(jù)alertViewDelegate代理回調(diào),處理不同的業(yè)務(wù),那么如何辨別是哪個(gè)alertView
- 一個(gè)簡單的方法就是給不同的alertView設(shè)置不同的tag,在alertViewDelegate代理回調(diào)發(fā)放中,根據(jù)不同的tag,if判斷一大推;
- 另外一個(gè)解決方法,利用運(yùn)行時(shí),給alertView擴(kuò)充一個(gè)分類,聲明一個(gè)傳入block參數(shù)的方法,設(shè)置alertView分類作為alertViewDelegate的代理,并且實(shí)現(xiàn)代理方法,利用運(yùn)行時(shí),拿到傳過來的block,并執(zhí)行,那么根據(jù)分類優(yōu)先原則,會優(yōu)先調(diào)用分類的方法,在alertView退出界面后,斷開管理;
直接上代碼
#import <UIKit/UIKit.h>
#import "objc/runtime.h"
typedef void(^blockCallBack)(NSInteger index);
@interface UIAlertView (BlockSupport)<UIAlertViewDelegate>
-(void)setCallBackBlock:(blockCallBack)block;
@end
1.設(shè)置關(guān)聯(lián)對象
-(void)setCallBackBlock:(blockCallBack)block
{
// static char kAssociatedObjectKey;
// 將某個(gè)值跟某個(gè)對象關(guān)聯(lián)起來重绷,將某個(gè)值存儲到某個(gè)對象中
// key:通常推薦key使用static char類型——使用指針或許更好,key值是一個(gè)唯一的常量膜毁,并只在getters和setters方法內(nèi)部使用objc_getAssociatedObject(self, &kAssociatedObjectKey);
// 一個(gè)更簡單的方案是:直接使用選擇器(selector)因?yàn)镾EL生成的時(shí)候就是一個(gè)唯一的常量昭卓,你可以使用 _cmd 作為objc_setAssociatedObject()的key
// value:需要保存的值,這里傳block,把block先保存起來,合適的地方調(diào)用
// policy:這里的policy跟屬性聲明中的retain、assign瘟滨、copy是一樣的候醒,不再贅述
// objc_setAssociatedObject(<#id object#>, <#const void *key#>, <#id value#>, <#objc_AssociationPolicy policy#>)
objc_setAssociatedObject(self, @selector(setCallBackBlock:), block, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
self.delegate=self;
}
2. 獲取相關(guān)聯(lián)的對象,執(zhí)行block
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
blockCallBack block=objc_getAssociatedObject(self, @selector(setCallBackBlock:));
!block ? : block(buttonIndex);
}
3.斷開關(guān)聯(lián)
-(void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
// 移除關(guān)聯(lián)對象 不恰當(dāng)?shù)淖龇? // 根據(jù)蘋果文檔描述,你不大可能有需求要自己去調(diào)用
// 因?yàn)樗矔瞥舭ㄆ渌胤郊尤氲娜康年P(guān)聯(lián)對象
// objc_removeAssociatedObjects(self);
// 一般你只需要通過調(diào)用objc_setAssociatedObject并傳入nil值來清除關(guān)聯(lián)值
objc_setAssociatedObject(self, @selector(setCallBackBlock:), nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
4.用法示例
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"測試提示" message:@"這是船長的demo" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"確定", nil];
[alert setCallBackBlock:^(NSInteger index) {
if (index != alert.cancelButtonIndex) {
NSLog(@"點(diǎn)擊了確定按鈕");
}
}];
[alert show];
}
- 舉一反三,大家也可以聯(lián)想到
UIActionSheet
等其他控件,給他們也寫個(gè)分類,方便我們使用