Write in the first【写在最前】
Notification(通知) 是 iOS 系统下重要的消息传递机制之一,通知封装了诸如窗口获得焦点、网络连接关闭等事件信息,通知的内容可按照我们实际的需求来定制。在实际开发中或多或少都会接触到,NSNotificationCenter 与 其它对象之间通信方式类似,但也存在不同,我们需要根据具体应用场景选择(或优先选择)恰当的通信方式。
本篇文章主要从【NSNotification和NSNotificationCenter 使用和注意点】学习总结。
在「时间 & 知识 」有限内,总结的文章难免有「未全、不足 」的地方,还望各位好友指出,以提高文章质量。
目录:
- NSNotification
1.NSNotification 概念
2.NSNotification.h 系统文件- NSNotificationCenter
1.NSNotificationCenter 概念
2.NSNotificationCenter 系统文件- NSNotificationCenter 的使用流程
1.注册观察者(添加监听)
2.发送通知
3.移除观察者(移除监听)- NSNotificationQueue
1.NSNotificationQueue 概念
2.NSNotificationQueue.h 系统文件
3.NSNotificatinonCenter 实现原理- NSNotificatinonCenter 实战使用
1.基本使用
2.通知在多线程中使用
3.通知在多线程中注意点- NSNotification Demo效果图
- 期待 & 后续 & About me
NSNotification
本着好好学习,了解权威的目的,我们还是主动看官网的说明。
NSNotification 概念
上图简单点释义就是NSNotification
是方便NSNotificationCenter
广播到其他对象时的封装对象,简单讲即通知中心对通知调度表中的对象广播时发送NSNotification
对象。NSNotification
对象(称为通知)包含名称、object
和一个可选字典三个属性,名称是用来标识通知的标记(一般为常量字符串),object
是任意想要携带的对象(通常为发送者自己 或为nil),字典用来存储发送通知时附带的信息(可为nil)。NSNotification
对象是不可变的对象。
NSNotification.h 系统文件
看完官网,接下来当然是看系统文件了。你说是吧!
|
|
接下来看下三个初始化方法
|
|
NSNotificationCenter
我们还是主动看官网的说明。
NSNotificationCenter 概念
上图简单点释义就是NSNotificationCenter
对象(通知中心) 是 Foundation 框架的一个子系统,提供了在程序中广播消息的机制。通过[NSNotificationCenter defaultCenter]
获取引用总的通知中心,可以在不同类之间通信的时候使用。
在通知中心注册观察者,发送者使用通知中心广播时,以NSNotification
的name
和object
来确定需要发送给哪个观察者。为保证观察者能接收到通知,所以应先向通知中心注册观察者,接着再发送通知这样才能在通知中心调度表中查找到相应观察者进行通知。
NSNotificationCenter 系统文件
看完官网,接下来当然是看系统文件了。你说是吧!
|
|
总结:获取NSNotificationCenter
的方法只有一种,即[NSNotificationCenter defaultCenter]
,并且NSNotificationCenter
是一个单例模式,一旦创建,这个通知中心的对象会一直存在于一个应用的生命周期。
通知中心的使用流程
获取通知中心对象后,我们就可以使用它来处理通知相关的操作了,包括注册观察者、发送通知 和 移除观察者。
1、注册观察者(添加监听)
你可以使用以下两种方式注册观察者
|
|
总结:
第一种方式是比较常用的添加
Oberver
的方式,接到通知时执行Selector
方法,观察者接收到通知后执行任务的代码在发送通知的线程中执行(下面示例代码验证)。第二种方式是提供了一个以
block
方式实现的添加观察者的方法。大家第一次看到这个方法时是否会有这样的疑问:观察者呢?参数中并没有指定具体的观察者,那谁是观察者呢?需要移除吗?实际上,与前一个方法不同的是,前者使用一个现存的对象作为观察者,而这个方法会创建一个匿名的对象作为观察者(即方法返回的
id<NSObject>
对象),这个匿名对象会在指定的队列(queue)
上去执行我们的block
。
第二种注册方式注意点:
1、参数
queue
决定block
在哪个线程执行,即我们指定了操作队列。如果queue
为nil
,则消息是默认在post
线程中同步处理,即 观察者接收到通知后执行任务的代码在发送通知的线程中执行(下面示例代码验证)。2、
block
块会被通知中心拷贝一份(执行copy操作
),以在堆中维护一个block
对象,直到观察者被从通知中心中移除。所以,应该特别注意在block
中使用外部对象,避免出现对象的循环引用。3、如果一个给定的通知触发了多个观察者的
block
操作,则这些操作会在各自的Operation Queue
中被并发执行。所以我们 不能去假设操作的执行会按照添加观察者的顺序来执行。4、该方法会返回一个表示观察者的对象,记得在不用时移除这个对象。
第2点示例,由于使用的是block,所以需要注意的就是避免引起循环引用的问题:
|
|
我们可以看到createObserver
中创建的observer
并没有被释放。所以,使用addObserverForName:object:queue:usingBlock:
一定要注意这个问题。
2、发送通知
发送通知可使用以下方法
总结:
三种方式都是发送
NSNotification
对象给通知中心注册的所有观察者。发送通知通过
name
和object
来确定来标识观察者,name
和object
两个参数的规则相同即当通知设置name
为kChangeNotifition
时,那么只会发送给符合name
为kChangeNotifition
的观察者,同理object
指发送给某个特定对象通知,如果只设置了name
,那么只有对应名称的通知会触发。如果同时设置name
和object
参数时就必须同时符合这两个条件的观察者才能接收到通知。
3、移除观察者(移除监听)
在对象被释放前需要移除掉观察者,避免已经被释放的对象还接收到通知导致崩溃。
移除观察者有两种方式:
|
|
总结:
传入相应的需要移除的
observer
或者使用第二种方式三个参数来移除指定某个观察者。如果使用基于
-[NSNotificationCenter addObserverForName:object:queue:usingBlock:]
方法在获取方法返回的观察者进行释放。基于这个方法我们还可以让观察者接到通知后只执行一次:1234__block __weak id<NSObject> observer = [[NSNotificationCenter defaultCenter] addObserverForName:@"note" object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {NSLog(@"%@",[NSThread currentThread]);}];通知的发送与处理是同步的,在某个地方
post
一个消息时,会等到所有观察者对象执行完处理操作后,才回到post
的地方,继续执行后面的代码。
|
|
NSNotificationQueue
我们还是主动看官网的说明。
NSNotificationQueue 概念
上图简单点释义就是
NSNotificationQueue 通知队列,更像是通知中心的缓冲区,用来管理多个通知的调用。通知队列通常以先进先出(FIFO)顺序管理通知。当一个通知上升到队列的前面时,队列就将它发送给通知中心(NSNotificationCenter
),通知中心随后将它派发给所有注册为观察者的对象。。
NSNotificationQueue.h 系统文件
看完官网,接下来当然是看系统文件了。你说是吧!
- 创建通知队列方法
|
|
- 往队列加入通知方法(异步)
|
|
- 移除队列中的通知方法
|
|
- 发送方式
|
|
- 合并通知
- 通过合并我们可以用来保证相同的通知只被发送一次。
forModes:(nullable NSArray<NSRunLoopMode> *)modes
可以使用不同的NSRunLoopMode
配合来发送通知,可以看出实际上NSNotificationQueue
与RunLoop
的机制以及运行循环有关系,通过NSNotificationQueue
队列来发送的通知和关联的RunLoop
运行机制来进行的。
|
|
NSNotificatinonCenter 实现原理
NSNotificatinonCenter
是使用观察者模式来实现的用于跨层传递消息,用来降低耦合度。NSNotificatinonCenter
用来管理通知,将观察者注册到NSNotificatinonCenter
的通知调度表中,然后发送通知时利用标识符name
和object
识别出调度表中的观察者,然后调用相应的观察者的方法,即传递消息(在Objective-C
中对象调用方法,就是传递消息,消息有name或者selector
,可以接受参数,而且可能有返回值),如果是基于block
创建的通知就调用NSNotification
的block
。
NSNotificatinonCenter 实战使用
基本使用
第一种注册观察者的方式:
|
|
第二种注册观察者的方式:
|
|
总结:这个方法的优点在于添加观察者的操作与回调处理操作的代码更加紧凑,不需要拼命滚动鼠标就能直接找到处理代码,简单直观。个人比较喜欢。
通知在多线程中使用
注册观察者两种方式,在多线程中的使用和注意点,以下 会分别说明。
第一种注册观察者方式,以下代码我们将验证这个结论:
接收通知代码 由 发出通知线程决定,即观察者接收到通知后执行任务的代码在发送通知的线程中执行
|
|
|
|
第二种注册观察者方式,以下代码我们将验证这个结论:
接收通知代码 由 发出通知线程决定,即观察者接收到通知后执行任务的代码在发送通知的线程中执行(指定操作队列除外)
|
|
|
|
通知在多线程中注意点
开发中使用场景,第一种方式,一般在接收通知执行代码中,做一下处理:
|
|
第二种方式,一般在接收通知执行代码中,做一下处理:
|
|
总结
在我们的应用程序中,两个对象之间如何通信。根据具体应用场景优先选择哪一种通信方式。
对象之间的通信方式主要有以下几种:
- 直接方法调用
Target-Action事件
Delegate
代理block
回调KVO
监听NSNotification
通知
优先选择哪一种通信方式:
通信对象是一对一的还是一对多的
对象之间的耦合度.
建议:
- 1.在需要的地方使用通知,要求: 必须得保证通知的名称在监听和发出时是一致的。
- 2.注册的观察者在不使用时一定要记得移除,即添加和移除要配对出现。
- 3.尽可能迟地去注册一个观察者,并尽可能早将其移除,这样可以改善程序的性能。因为,每
post
一个通知,都会是遍历通知中心的分发表,确保通知发给每一个观察者。 - 4.记住通知的发送和处理是在同一个线程中。
- 5.使用
-addObserverForName:object:queue:usingBlock:
务必处理好内存问题,避免出现循环引用。 - 6.
NSNotificationCenter
是线程安全的,但并不意味着在多线程环境中不需要关注线程安全问题。不恰当的使用仍然会引发线程问题。
期待
- 如果在阅读过程中遇到 error || new ideas,希望你能 messages 我,我会及时改正谢谢。
- 点击右上角的 喜欢 和 订阅Rss 按钮,可以收藏本仓库,并在 Demo 更新时收到邮件通知。