iOS中NSNotification的套路

1,652 阅读2分钟

常见的使用就不说了,说一说自己遇到的tip

添加和移除

  • 多次添加会多次收到消息,所以确保添加和移除是一一对应的。

同步发送通知

这一点与KVO类似,即发送通知的时候,所有接收者都收到通知才算做发送完成。

发送通知与响应通知都在一个线程

这一点与KVO类似。 NSNotification不能跨线程:即响应通知的action,默认是与postNotification在同一个线程的,若想在指定线程中执行响应通知的方法,可以使用带有block的addObserver方法,或者使用dispatch_async(xxx)。

如果需要跨线程,可以使用带有block的通知接口。

如AFN中:

dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
            if (self.completionHandler) {
                self.completionHandler(task.response, responseObject, error);
            }

            dispatch_async(dispatch_get_main_queue(), ^{
                [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
            });
        });

为了保证在主线程发送通知。 这一点可以在一些网络请求数据到达时, 用于刷新界面。

dispatch_async(dispatch_get_main_queue(), ^{
        [[NSNotificationCenter defaultCenter] postNotificationName:kNotificationFilterMaterialRequestFinished object:self userInfo:nil];
    });

线程安全及一个死锁案例

object,observer的weak还是unsafe

为何SDWebImage中对object使用weakSelf

SDWebImage的改动

FREQUENT CRASH in [SDWebImageDownloaderOperation cancelInternal] #1807

在指定线程接收通知

  1. 使用queue和block

  2. 使用mach port

上面说到,收发都在一个线程中,如果想要做到,在某个指定的线程接收通知,该如何做呢?苹果文档上提及了实现思路。

先简单介绍下mach port,它主要用来线程间通信。简单来说,就是接收线程中注册NSMachPort,在另外的线程中使用此port发送消息,则注册线程会收到相应消息,调用handleMachMessage来处理。

主要思路:

定义一个中间对象NotificationHandler,用来专门接收通知,包括一个队列,接收通知的线程,mach port,lock。

首先NotificationHandler会注册一个通知,对应的处理函数为processNotification,当在其他线程中post时,processNotification会被调用,进行如下处理。如果收到的通知跟指定的线程一样,则处理消息,反之,则添加到队列,同时通过port发送消息给指定线程。注意多线程中,对队列的处理,要加锁。

指定线程收到回调handleMachMessage,首先会将通知删除,然后调用processNotification进行处理,继续以上过程。

参考

你可能不知道的 Notification

NSNotification 线程管理以及自动注销开源方案