UIAlertController/UIAlertView
自從iOS8之后蘋果推出了UIAlertController代替UIAlertView贱除,慢慢的UIAlertView也逐漸“失了寵”被deprecated了塘揣。當(dāng)然在使用的過程中我們也發(fā)現(xiàn)了UIAlertController的優(yōu)勢屠阻,自由度更高炕贵,封裝起來更方便于置,至少不用再像UIAlertView那樣設(shè)置代理穿香,實現(xiàn)代理方法那么繁瑣了亭引,所有的操作都可以添加UIAlertAction通過block來進(jìn)行操作。但是有的比較老的項目還要適配iOS7(當(dāng)然這種情況是越來越少了)皮获,UIAlertController在iOS8之前的版本里邊就不能用了焙蚓,然后我就想能不能封裝一個工具類,在iOS8以前的系統(tǒng)里邊使用UIAlertView魔市,之后的使用UIAlertController主届。一直覺得iOS中恰當(dāng)?shù)氖褂胋lock是一件非常優(yōu)雅的事情,當(dāng)然要注意循環(huán)引用的問題待德,所以就想用block來進(jìn)行封裝君丁,代替代理那一套繁瑣的東西。
AlertTool
之前在學(xué)習(xí)runtime相關(guān)知識的時候将宪,看到關(guān)聯(lián)對象這個知識點绘闷,可以完美的解決這個問題橡庞。創(chuàng)建一個UIAlertView的分類
上代碼:
#import <UIKit/UIKit.h>
typedef void (^alertBlock)(NSInteger selectedIndex);
@interface UIAlertView (Category)<UIAlertViewDelegate>
- (void)showAlertView:(alertBlock)block;
@end
然后用associateObject把alertBlock與UIAlertView關(guān)聯(lián)起來
- (void)showAlertView:(alertBlock)block{
if (block) {
objc_setAssociatedObject(self, &alertKey, block, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
self.delegate = self;
}
[self show];
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
alertBlock block = objc_getAssociatedObject(self, &alertKey);
if (block) {
block(buttonIndex);
}
}
然后封裝出來一個工具類alertTool
+ (void)showAlertWithTitle:(NSString *)title message:(NSString *)message confirmBlock:(void (^)())confirmBlock cancelBlock:(void (^)())cancelBlock{
NSString *version = [UIDevice currentDevice].systemVersion;
NSString *alertTitle = !title ? title : @"提示";
NSString *alertMess = !message ? message : @"提示信息";
if (version.floatValue >= 8.0) {
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:alertTitle message:alertMess preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *confirmAction = [UIAlertAction actionWithTitle:@"確定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
confirmBlock();
}];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
cancelBlock();
}];
[alertController addAction:cancelAction];
[alertController addAction:confirmAction];
[[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alertController animated:YES completion:nil];
}else{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:alertTitle message:alertMess delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"確定", nil];
[alertView showAlertView:^(NSInteger selectedIndex) {
if (selectedIndex == 1) {
confirmBlock();
}else{
cancelBlock();
}
}];
}
}
以為這里是一個類方法,所以也不用考慮循環(huán)引用的問題AlertTool Demo印蔗。其實看到這里就會發(fā)現(xiàn)真正的主角是objc_setAssociatedObject/objc_getAssociatedObject
objc_setAssociatedObject/objc_getAssociatedObject
上邊兩個函數(shù)的使用需要導(dǎo)入<objc/runtime.h>扒最,網(wǎng)上關(guān)于關(guān)聯(lián)對象的文章很多,也分析的很透徹华嘹。而且發(fā)現(xiàn)很多優(yōu)秀的三方庫中都有用到這個方法吧趣,我就結(jié)合自己的理解總結(jié)一下,以便以后學(xué)習(xí)耙厚、使用强挫。
就從一個常見的面試題開始 --- 可以在分類中給對象添加屬性嗎?雖然在分類中聲明的屬性在別的類中也能通過點語法獲取到薛躬,但是會warning沒有實現(xiàn)setter/getter方法俯渤,運行的時候也會crash。其實嚴(yán)格意義上來講是不可以的型宝,關(guān)聯(lián)對象卻可以在某種角度來幫我們“實現(xiàn)”這個功能八匠。主要有三個方法:
-
void objc_setAssociatedObject(id object, void *key, id value, objc_AssociationPolicy)
此方法用于通過使用給定的鍵和策略為某對象設(shè)置關(guān)聯(lián)對象值,傳入 nil 則可以移除已有的關(guān)聯(lián)對象 -
id objc_getAssociatedObject(id object, void *key)
此方法用于通過使用給定的鍵獲取關(guān)聯(lián)對象值 -
void objc_removeAssociatedObjects(id object)
移除指定對象所有關(guān)聯(lián)對象趴酣,不常用
使用過程中梨树,如果想要兩個鍵匹配同一個值,則二者必須是完全相同的指針岖寞,即set和get中兩個key必須完全相同劝萤,所以一般會使用靜態(tài)全局變量作為鍵。在分類中添加屬性時慎璧,還可以使用“屬性”getter方法名做key,這樣省得去定義變量跨释。
- (NSString *)propertyName {
return objc_getAssociatedObject(self, _cmd);
}
- (void)setAssociatedObject_assign:(NSString *)associatedObject_assign {
objc_setAssociatedObject(self, @selector(propertyName), associatedObject_assign, OBJC_ASSOCIATION_ASSIGN);
}
其實到這里就可以調(diào)用對象點語法去獲取設(shè)置屬性值和獲取屬性值了胸私,但其實這跟我們平時在類中定義的屬性是有區(qū)別的。我們在類中聲明的屬性系統(tǒng)會幫我們生成下劃線開頭的同名的成員變量鳖谈,實現(xiàn)setter/getter方法岁疼。分類中我們雖然手動實現(xiàn)了setter/getter方法,但是沒有并沒有生成成員變量缆娃,你在setter方法中也無法訪問到該成員變量捷绒,具體驗證可以看iOS分類不能添加屬性原因的探索。
總的來說贯要,在分類里使用@property聲明屬性暖侨,只是將該屬性添加到該類的屬性列表,并聲明了setter和getter方法崇渗,但是沒有生成相應(yīng)的成員變量字逗,也沒有實現(xiàn)setter和getter方法京郑。但我們手動實現(xiàn)了setter和getter方法也就可以在別的類中通過點語法給該屬性賦值和取值,但是依然沒有相應(yīng)的成員變量葫掉,當(dāng)然你可以自己生成一個成員變量些举,這也是前邊“實現(xiàn)”要加引號的原因。
關(guān)聯(lián)對象還可以用來傳值俭厚,某些場景下可以免去一些繁瑣的步驟户魏,挺好用的工具。其實覺得得runtime里邊的東西越來越有趣挪挤,我也會繼續(xù)研究下去叼丑。理解的比較粗淺,有錯誤的地方歡迎大家指正电禀。
參考:http://blog.leichunfeng.com/blog/2015/06/26/objective-c-associated-objects-implementation-principle/
http://www.reibang.com/p/79479a09a8c0