NSNotification

啥时候用:

没有明显层级关系的时候用.

例如在推送通知的时候,因为我们并不知道在什么时间点会触发推送,也不知道触发时当前应用出于哪一个页面,所以使用通知.相反,如果此时通过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

该技巧可以让我们只注册一次通知,使用完毕后,即被删除.

有两点需要注意.

  1. block的的notification的返回值,前面是否带__block关键字是完全不一样的,决定block对于返回值的捕获.

  2. 除了带上__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南峰子

你可能感兴趣的:(NSNotification)