啥时候用:
没有明显层级关系的时候用.
例如在推送通知的时候,因为我们并不知道在什么时间点会触发推送,也不知道触发时当前应用出于哪一个页面,所以使用通知.相反,如果此时通过navgation controller遍历节点,查询相关节点,做出相应操作就不太好了.
在有明显层级关系的时候,例如A页面需要选择城市,点击按钮后弹出B城市筛选页面,选择城市后B页面消失,A页面做出相应操作.这种情况就不适合.使用Delegate/KVO/Block均好于Notification
取消地点:
在UIViewController中,如果可以不再dealloc方法里面取消,尽量不要在.
如果可以的话,尽量在Appear(will/did)的时候进行Add/Remove Observer.当然要配对好.
dealloc是个挺危险的方法.相信每个人都有各式各样的问题导致内存没有释放.尽管这可以通过查找内存泄露来解决.
但是泄露内存随着iPhone硬件的提升,很多时候不并不会造成严重的后果.但是重复的通知发送,可能会导致严重而又诡异的bug.
尽量在安全的地方Remove.当然,这并不代表我们可以忽略对内存的管理.不是这个意思...
安全删除:
在一个Object注册了很多个通知的情况下,有人喜欢用[[NSNotificationCenter defaultCenter] removeObserver:self]来一次性删除.
但,真的好么?
不好.
因为它无法删除带有block的Notifcation.
尽量配对的好,有添也有删.
Block的Notification:
指的是这个方法:
- (id )addObserverForName:(NSString *)name object:(id)obj queue:(NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block
使用weak/strong大法.不然在调用self等的时候,会造成引用循环.
似乎印象中,系统的block不会造成引用循环,比如UIView的animation.别经验主义,为了保证安全,我们甚至可以在所有的block中均使用weak/strong大法.
它会返回一个observer对象,我们需要记着删除他.当然,在这之前我们需要根据业务逻辑来决定持有该对象多久.
Notification Once:
详见:Nofication Once---by孙源
在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 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
该技巧可以让我们只注册一次通知,使用完毕后,即被删除.
有两点需要注意.
block的的notification的返回值,前面是否带__block关键字是完全不一样的,决定block对于返回值的捕获.
除了带上__block,尽量带上__weak或者在block中手动将返回值置为nil
利用ARC更好的使用Notication
Notification最常见的问题就是忘记删除.当出现此类问题的时候,非常不好排查.而引发的问题可能是巨大的,并且诡异难以debug.
所以结合ARC的特性,可以更优雅的使用Notification.
思路是我们通过一个对象来管理通知.在注册通知的时候,通过一定的数据结构作好记录.在对象dealloc的时候,删除记录的通知对象.
因为ARC,所以我们不需要手动显示的释放该对象.该对象释放的时候,也就相应的删除了通知.
当然,github上有更多更好的轮子.在实际生产中,或许我们可以使用已经造好的轮子.
线程:
带有block的Notification,有一个参数是queue.意思是指定执行block的线程.如果没有指定,则在post对象的线程中执行.
所以,如果没有指定线程的话,那么这个block可能会在任何一个线程中执行,这完全取决于谁在post.
所以,如果是ui相关操作的话,最好我们指定为主线程.
另外,有A,B 2个对象,分别处于2个线程中.A post了一个notification,在notification中将会使用C对象执行一系列方法.而B对象恰恰又释放了C
对象.那么此时会不会出现问题.
答案是会的.
怎么办呢?
尽量使用block的Notification,毕竟block会捕获相关变量.
更多的线程请参考Notification与多线程---by南峰子