啥時(shí)候用:
沒有明顯層級(jí)關(guān)系的時(shí)候用.
例如在推送通知的時(shí)候,因?yàn)槲覀儾⒉恢涝谑裁磿r(shí)間點(diǎn)會(huì)觸發(fā)推送,也不知道觸發(fā)時(shí)當(dāng)前應(yīng)用出于哪一個(gè)頁面,所以使用通知.相反,如果此時(shí)通過navgation controller遍歷節(jié)點(diǎn),查詢相關(guān)節(jié)點(diǎn),做出相應(yīng)操作就不太好了.
在有明顯層級(jí)關(guān)系的時(shí)候,例如A頁面需要選擇城市,點(diǎn)擊按鈕后彈出B城市篩選頁面,選擇城市后B頁面消失,A頁面做出相應(yīng)操作.這種情況就不適合.使用Delegate/KVO/Block均好于Notification
取消地點(diǎn):
在UIViewController中,如果可以不再dealloc方法里面取消,盡量不要在.
如果可以的話,盡量在Appear(will/did)的時(shí)候進(jìn)行Add/Remove Observer.當(dāng)然要配對(duì)好.
dealloc是個(gè)挺危險(xiǎn)的方法.相信每個(gè)人都有各式各樣的問題導(dǎo)致內(nèi)存沒有釋放.盡管這可以通過查找內(nèi)存泄露來解決.
但是泄露內(nèi)存隨著iPhone硬件的提升,很多時(shí)候不并不會(huì)造成嚴(yán)重的后果.但是重復(fù)的通知發(fā)送,可能會(huì)導(dǎo)致嚴(yán)重而又詭異的bug.
盡量在安全的地方Remove.當(dāng)然,這并不代表我們可以忽略對(duì)內(nèi)存的管理.不是這個(gè)意思...
安全刪除:
在一個(gè)Object注冊(cè)了很多個(gè)通知的情況下,有人喜歡用[[NSNotificationCenter defaultCenter] removeObserver:self]來一次性刪除.
但,真的好么?
不好.
因?yàn)樗鼰o法刪除帶有block的Notifcation.
盡量配對(duì)的好,有添也有刪.
Block的Notification:
指的是這個(gè)方法:
- (id <NSObject>)addObserverForName:(NSString *)name object:(id)obj queue:(NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block
使用weak/strong大法.不然在調(diào)用self等的時(shí)候,會(huì)造成引用循環(huán).
似乎印象中,系統(tǒng)的block不會(huì)造成引用循環(huán),比如UIView的animation.別經(jīng)驗(yàn)主義,為了保證安全,我們甚至可以在所有的block中均使用weak/strong大法.
它會(huì)返回一個(gè)observer對(duì)象,我們需要記著刪除他.當(dāng)然,在這之前我們需要根據(jù)業(yè)務(wù)邏輯來決定持有該對(duì)象多久.
Notification Once:
在AFNetworking中也能看到此代碼,UIAlertView+AFNetworking.h中的方法:
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000
+ (void)showAlertViewForTaskWithErrorOnCompletion:(NSURLSessionTask *)task
delegate:(id)delegate
cancelButtonTitle:(NSString *)cancelButtonTitle
otherButtonTitles:(NSString *)otherButtonTitles, ... NS_REQUIRES_NIL_TERMINATION
{
NSMutableArray *mutableOtherTitles = [NSMutableArray array];
va_list otherButtonTitleList;
va_start(otherButtonTitleList, otherButtonTitles);
{
for (NSString *otherButtonTitle = otherButtonTitles; otherButtonTitle != nil; otherButtonTitle = va_arg(otherButtonTitleList, NSString *)) {
[mutableOtherTitles addObject:otherButtonTitle];
}
}
va_end(otherButtonTitleList);
__block __weak id<NSObject> observer = [[NSNotificationCenter defaultCenter] addObserverForName:AFNetworkingTaskDidCompleteNotification object:task queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notification) {
NSError *error = notification.userInfo[AFNetworkingTaskDidCompleteErrorKey];
if (error) {
NSString *title, *message;
AFGetAlertViewTitleAndMessageFromError(error, &title, &message);
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:nil message:nil delegate:delegate cancelButtonTitle:cancelButtonTitle otherButtonTitles:nil, nil];
for (NSString *otherButtonTitle in mutableOtherTitles) {
[alertView addButtonWithTitle:otherButtonTitle];
}
[alertView setTitle:title];
[alertView setMessage:message];
[alertView show];
}
[[NSNotificationCenter defaultCenter] removeObserver:observer];
}];
}
#endif
該技巧可以讓我們只注冊(cè)一次通知,使用完畢后,即被刪除.
有兩點(diǎn)需要注意.
block的的notification的返回值,前面是否帶__block關(guān)鍵字是完全不一樣的,決定block對(duì)于返回值的捕獲.
除了帶上__block,盡量帶上__weak或者在block中手動(dòng)將返回值置為nil
利用ARC更好的使用Notication
Notification最常見的問題就是忘記刪除.當(dāng)出現(xiàn)此類問題的時(shí)候,非常不好排查.而引發(fā)的問題可能是巨大的,并且詭異難以debug.
所以結(jié)合ARC的特性,可以更優(yōu)雅的使用Notification.
思路是我們通過一個(gè)對(duì)象來管理通知.在注冊(cè)通知的時(shí)候,通過一定的數(shù)據(jù)結(jié)構(gòu)作好記錄.在對(duì)象dealloc的時(shí)候,刪除記錄的通知對(duì)象.
因?yàn)锳RC,所以我們不需要手動(dòng)顯示的釋放該對(duì)象.該對(duì)象釋放的時(shí)候,也就相應(yīng)的刪除了通知.
當(dāng)然,github上有更多更好的輪子.在實(shí)際生產(chǎn)中,或許我們可以使用已經(jīng)造好的輪子.
線程:
帶有block的Notification,有一個(gè)參數(shù)是queue.意思是指定執(zhí)行block的線程.如果沒有指定,則在post對(duì)象的線程中執(zhí)行.
所以,如果沒有指定線程的話,那么這個(gè)block可能會(huì)在任何一個(gè)線程中執(zhí)行,這完全取決于誰在post.
所以,如果是ui相關(guān)操作的話,最好我們指定為主線程.
另外,有A,B 2個(gè)對(duì)象,分別處于2個(gè)線程中.A post了一個(gè)notification,在notification中將會(huì)使用C對(duì)象執(zhí)行一系列方法.而B對(duì)象恰恰又釋放了C
對(duì)象.那么此時(shí)會(huì)不會(huì)出現(xiàn)問題.
答案是會(huì)的.
怎么辦呢?
盡量使用block的Notification,畢竟block會(huì)捕獲相關(guān)變量.
更多的線程請(qǐng)參考Notification與多線程---by南峰子