Swift之正确使用通知
本文主要介绍,如何在项目中正确的使用通知,在使用之前,先了解几个概念:
概念一:NotificationCenter(通知中心)
通知中心实际上是iOS程序内部之间的一种消息广播机制,主要为了解决应用程序内部不同对象之间解耦而设计。它是基于观察者模式设计的,不能跨应用程序进程通信,当通知中心接收到消息之后会根据内部的消息转发表,将消息发送给订阅者。
通知是同步的,接收通知的对象会在发送通知对象的线程中执行方法,当所有接收对象执行完消息后,发送通知对象才会执行接下来的代码。
每一个iOS程序都有一个唯一的通知中心,它是系统创建好的一个单例类,不需要我们自己去创建。
1.获取通知中心
class var default: NotificationCenter{ get }
2.在通知中心注册一个通知
func addObserver(_ observer: Any, selector aSelector: Selector, name aName: NSNotification.Name?, object anObject: Any?)
概念二:Notification(通知)
Notification就是我们发送的消息对象,有以下三个常用的属性:
1.通知的名称,用来标示一个通知,一般为字符串
public var name:Notification.Name
2.任意想要携带的对象,通常为发送者自己
public var object:Any?
3.附加信息
public var userInfo: [AnyHashable: Any]?
概念三:移除通知
在通知中心注册通知后,当注册对象销毁时如果不在通知中心中移除通知,当收到通知时,会造成向野指针对象发送消息,会crash。
1.移除所有通知
func removeObserver(_observer:Any)
2.根据通知名称,移除通知
func removeObserver(_observer:Any, name aName:NSNotification.Name?, object anObject:Any?)
下面介绍项目中如何正确的使用通知
方式一:异步发送通知
上面说过,通知是同步的,它会等到所有接受者执行完代码后,才会执行接下来的代码,如果在当前线程直接发送通知,就会阻塞当前线程,所以将发送通知放在全局并发队列中:
1.发送通知
DispatchQueue.global().async{
NotificationCenter.default.post(name:NSNotification.Name(rawValue: "name"),
object:nil,
userInfo:nil)
}
2.注册通知
NotificationCenter.default.addObserver(self,
selector:#selector(login(_:)),
name:NSNotification.Name(rawValue:"name"),
object:nil
)
3.收到通知时,执行的方法
func login(_notification:Notification) {
DispatchQueue.main.async{
// 如果更新UI,需要在主线程中
}
}
4.移除通知
NotificationCenter.default.removeObserver(self, name:NSNotification.Name(rawValue:"name"), object:nil)
方式二:使用通知队列发送通知
NotificationQueue(通知队列)
通知中心收到发送者发出的通知后,会立刻分发给监听者,但是如果把通知放在通知队列中,通知就可以等到某些特定时刻再发出,比如等到之前发出的通知在runloop中处理完,或者runloop空闲的时候。它就像通知中心的缓冲池,把一些不着急发出的通知存在通知队列中。
存储在通知队列中的通知会以先进先出的方式发出。
每个线程有一个默认的通知队列,它和通知中心关联着,是一个单例类。你也可以自己为线程或者通知中心创建多个通知队列。
1.获取通知队列
class var `default`: NotificationQueue { get }
2.通知队列发送通知
func enqueue(_ notification: Notification, postingStyle: NotificationQueue.PostingStyle, coalesceMask: NotificationQueue.NotificationCoalescing, forModes modes: [RunLoopMode]?)
参数介绍:
notification:要发送的通知消息对象
postingStyle:发送方式,有三种:whenIdle(空闲时发送)、asap(尽快发送)、now(立即发送)
coalesceMask:过滤通知方式,有三种:none(不合并)、onName(按通知的名字合并)、onSender(按通知的发送者合并)
forModes:指定了某种特定runloop mode,一般为nil
下面用通知队列发送一个消息,接收通知的代码就不写了,和方式一里面的一样:
// 初始化消息通知对象
let notification = Notification.init(name:Notification.Name(rawValue: "name"),
object:nil,
userInfo: userInfo)
// 获取通知队列
let nq = NotificationQueue.default
// 发送通知
nq.enqueue(notification,
postingStyle:NotificationQueue.PostingStyle.asap,// 发送方式,立即发送
coalesceMask:NotificationQueue.NotificationCoalescing.onName,// 根据通知名称过滤
forModes:nil)
具体使用哪种方式,要根据项目业务逻辑判断。如果通知发送时间间隔短,比较频繁,优先级不高,简易使用通知队列,因为有过滤功能,可以选择性过滤,减少频繁发送通知带来的额外开销。