Notification Programming

Notification Programming

通知机制避免了事件发生时传递消息发生紧耦合,在传递消息的同时保持了对象之间的独立,观察者会对感兴趣的事件作出反应,发布通知的对象不需要了解观察者的任何信息,而观察者至少需要知道通知名字和通知对象。
通知中心将通知同步发送给观察者。 换句话说,同步发布通知时,语句执行的控制权不会移交给发布者,直到所有观察者都收到并处理了通知。 要异步发送通知,请使用Notification Queues,在通知队列中进行了介绍。在多线程应用程序中,通知始终在发布通知的这个线程中传递。

/*
# 请记住,例如 可以通过使用协议等设计模式来松散耦合,特别是模型层和控制层的事件交流——控制器和/或模型层对象。
- 那什么时候使用通知中心呢(原则)
1. 当沟通需要反复发生并且保持一致时。(决定性的原因,高速公路,可以快速的交流)
2. 当使用一对多或多对多沟通时,通知中心是多对多。
3. 当应用程序的两个或多个组件之间需要通信时,它们之间没有正式的连接。
*/

So! Notification Center. Now you know… Here’s a quick summary:

  • First, register an observer for a notification with: addObserver(_:selector:name:object:)
  • Then, post a notification with post(name:object:userInfo:) …
  • … after which your selector is called
  • And don’t forget to remove the observer with removeObserver(...)

用通知中心方法注册本地通知观察者
通知中心管理着注册着观察者的表并通过通知调用链将收到的通知参数发送给满足特定条件的观察者。未注册的通知者,不能使用通知调用链。
未注册的通知者,不能使用通知调用链。使用NotificationCenter的关键是您要在要观察的通知发生之前注册观察者。 当通知发布到通知中心,并且您尚未观察到时,您显然会错过该通知。
Tips:通知调用链 ——— 观察者先在通知中心注册,然后被观察对象发生变化就制定通知发送规则,根据规则再发送通知到通知中心,通知中心再在注册表中查找对应的已经注册的观察者,然后调用相应观察者的通知响应方法。完成这个过程后会执行post方法下面的语句,除非是异步而且还没发送通知中心正等待中才可以直接执行post下面的语句或者继续合并通知队列,跳过调用相应观察者的响应方法。
通知调用链根据注册通知的方法决定如何在线程中传递,提供观察者响应方法的形参的注册通知方法主要用于主线程处理通知,闭包形式参数的注册通知方法在处理异步发送来通知的时候比较方便。
一般在viewDidLoad() and dealloc, or viewWillAppear(:) and viewWillDisappear(:)注册和移除通知,观察者必须先被移除,否则可能会向不存在的对象发送消息。(移除可以一次移除多个或者指定移除某一个)
NotificationCenter.default.addObserver(self, selector: #selector(handleChangeNotification(_:)), name: Store.changedNotification, object: nil);

/*
object形式参数可以是任意的,主要用来过滤通知的发送对象(注册时没有过滤),包括注册的object形式参数(这里是帮助响应通知方法直接过滤了),一般都是指发送通知sender或者nil; userInfo形式参数 send extra data with the notification.🍬post方法中的参数会被系统包装成notification对象发送给注册的观察者对象。
*/

通知中心NotificationCenter.default(发送通知方式之一)
NotificationCenter.default.post(name: Store.changedNotification, object: notifying, userInfo: userInfo);
使用NotificationCenter发布的所有通知都是同步传递的,这意味着所有观察者会同时收到通知并执行所有代码,然后将控制权传递回通知的发布者(接下来其线程上的剩余的语句执行)。post过程中会自动生成Notification对象,不需要手动创建Notification通知对象。
Notification对象(称为通知)包含name,object和可选dictionary,该对象会随通知调用链传递到的合适selector方法中,即可以在闭包或者selector中使用的时候进行过滤筛选值。
Tips:通知调用链 ——— 观察者先在通知中心注册,然后被观察对象发生变化就制定通知发送规则,根据规则再发送通知到通知中心,通知中心再在注册表中查找对应的已经注册的观察者,然后调用相应观察者的通知响应方法。完成这个过程后会执行post方法下面的语句,除非是异步而且还没发送通知中心正等待中才可以直接执行post下面的语句或者继续合并通知队列,跳过调用相应观察者的响应方法。
selector方法的一般格式:”handleChangeNotification“ 、 “on did something” 、“did do something” 、 “has downloaded something”等格式命名通知方法。
Notification.Name的使用格式: 将通知名称作为静态常量添加到Notification.Name的扩展中更加方便.
extension Notification.Name {
static let didReceiveData = Notification.Name("didReceiveData")
static let didCompleteTask = Notification.Name("didCompleteTask")
static let completedLengthyDownload = Notification.Name("completedLengthyDownload")
}

NotificationQueue.default异/同步发送通知(发送通知方式之一)
在不同线程中notificationQueue都是不同的对象,NotificationQueue可以同/异步合并通知,和通知队列NotificationCenter.default相关联,充当通知中心的缓存区。
通知调用链根据注册通知的方法决定如何在线程中传递,提供观察者响应方法的形参的注册通知方法主要用于主线程处理通知,闭包形式参数的注册通知方法在处理异步发送来通知的时候比较方便。

open func addObserver(forName name: NSNotification.Name?, object obj: Any?, queue: OperationQueue?, using block: @escaping (Notification) -> Void) -> NSObjectProtocol

/*
# addObserver方法的形式参数
-queue: 决定接收通知的线程
-nil-与发通知的线程一致,
-currentQueue-与注册通知的线程一致,
-mainQueue-在主线程
-usingBlock: 在规定的线程回调收到的通知
*/
您可以将通知放入NSNotificationQueue中,然后在当前线程中异步的发送通知。调用这个2个方法之后,发布对象会立即调用其线程上的剩余的语句执行。
//返回当前线程的默认通知队列(一般用单例模式default),不同线程的NotificationQueue.default对象都是不同的。
let notificationQueue = NotificationQueue.default
enqueue(_ notification: Notification, postingStyle: NotificationQueue.PostingStyle)
enqueue(_ notification: Notification, postingStyle: NotificationQueue.PostingStyle, coalesceMask: NotificationQueue.NotificationCoalescing, forModes modes: [RunLoop.Mode]?)

NotificationQueue.default之异步合并通知
NotificationQueue都是先进先出的顺序维护通知的,当通知上升到队列的最前面时,队列将其发布到通知中心,通知中心将通知分派给所有注册为观察者的对象。
NotificationQueue 不能无限地合并postingStyle通知, 直到run loop结束或者run loop空闲才能合并通知队列中的通知。队列中的通知在合并后会很快发送到通知中心,还没有到队列中的通知不能合并,但是如果在等待中而且已经在队列中的通知可以合并。由于.now的postingStyle的通知队列互相之间不允许进行任何合并,但是仅可以将前面正在排队的【.whenIdle 和 .asap】通知与其合并成一条通知后发送到NotificationCenter,因为它们仍在队列中等待运行循环空闲。
总而言之,如果您始终将.now用作postingStyle,则NotificationQueue的行为本质上与将通知直接发布到NotificationCenter的行为相同,但是,如果您使用不同的发布方式,则排队的通知可能会合并为一个通知。
let notification = Notification(name: Notification.Name("MyValueChanged"))
NotificationQueue.default.enqueue(notification, postingStyle: .whenIdle, coalesceMask: .none, forModes: nil)
进到coalesceMask形参类型 中会发现是OptionSet类型,所以可以多个组合或单个来指明通知的相似标准。
postingStyle: .now 同步发送通知。.now的同时,为CoalesceMask属性指定.onName,它们将自动合并具有相同名称的所有通知,以防止观察者因重复的通知而超载。

/*
# postingStyle: 形式参数NotificationQueue.PostingStyle.whenIdle 和 .asap , .now。
# coalesceMask: 形式参数合并通知[NotificationQueue.NotificationCoalescing.onName , .onSenderh]和 .none。
## 【.whenIdle 和 .asap】都是异步执行的

  • NotificationQueue.PostingStyle.whenIdle 也是和coalesceMask参数一起使用,仅在RunLoop处于等待状态时。避免巨大的开销,比如键盘输入字符发送通知。

  • .PostingStyle.asap 通知发送很多,开销很大的时候使用这个,配合coalesceMask参数NotificationQueue.NotificationCoalescing.onName | .onSender 一起使用,最后将通知合并成一个发送。

  • .PostingStyle.now 不要求异步发送、用同步发送也可以使用默认的NotificationCenter.default.postNotification发布通知,但是用.now的时候可以通过合并删除相似的通知,因此也要和coalesceMask参数一起使用。
    */

1⃣️Consider the following snippet:
NotificationCenter.default.addObserver(forName: .test, object: nil, queue: nil) { (notification) in
print("Received notification")
}

let queue = NotificationQueue.default
queue.enqueue(Notification(name: .test), postingStyle: .now)
print("Posted")
queue.enqueue(Notification(name: .test), postingStyle: .now)
print("Posted")
queue.enqueue(Notification(name: .test), postingStyle: .now)
print("Posted")

This will print:
Received notification
Posted
Received notification
Posted
Received notification
Posted
Since the postingStyle of .now does not allow for any coaelscing.

2⃣️Now, consider:
let queue = NotificationQueue.default

queue.enqueue(Notification(name: .test), postingStyle: .whenIdle)
print("Posted")
queue.enqueue(Notification(name: .test), postingStyle: .whenIdle)
print("Posted")
queue.enqueue(Notification(name: .test), postingStyle: .whenIdle)
print("Posted")

3⃣️This will print:
Posted
Posted
Posted
Received notification

Since the three notifications can be coalesced before they are delivered when the run loop is idle. You would see the same behaviour with .asap

Finally, consider:
queue.enqueue(Notification(name: .test), postingStyle: .whenIdle)
print("Posted")
queue.enqueue(Notification(name: .test), postingStyle: .whenIdle)
print("Posted")
queue.enqueue(Notification(name: .test), postingStyle: .now)
print("Posted")

This will print:
Posted
Posted
Received notification
Posted

线程替代技术:idle time notification
对于相对短且低优先级的任务,idle time notification 允许你在应用不忙的时候进行任务。Cocoa 通过 NSNotificationQueue 对象提供 idle-time notifications。要请求一个 idle-time notificaiton,使用默认的 NSNotificationQueue 对象发送 NSPostWhenIdle 选项的通知。这个队列会延迟投递 (delivery) 你的通知对象,直到 run loop 对象闲置的时候。

补充
在Cocoa中有很多种两个对象进行通信的途径。当然也能进行直接消息传递。也有像目标-动作,代理,回调这些解耦,一对一的设计模式。KVO允许让很多对象订阅一个事件,但是它把这些对象都联系起来了。另一方面通知让消息全局广播,并且让有监听该广播的对象接收该消息。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,539评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,594评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,871评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,963评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,984评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,763评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,468评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,357评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,850评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,002评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,144评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,823评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,483评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,026评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,150评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,415评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,092评论 2 355

推荐阅读更多精彩内容

  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,105评论 1 32
  • 1.KVO,即:Key-Value Observing,它提供一种机制,当指定的对象的属性被修改后,则对象就会接受...
    BEYOND黄阅读 1,542评论 0 6
  • 概述 在多数移动应用中任何时候都只能有一个应用程序处于活跃状态,如果其他应用此刻发生了一些用户感兴趣的那么通过通知...
    莫离_焱阅读 6,515评论 1 8
  • 我们在开发程序的时候,程序内不同对象间的通信是不可避免的,iOS中主要有以下这些通信方式: 图中按照耦合度的强弱和...
    等开会阅读 8,230评论 9 48
  • 桂林山水甲天下,贵州山水甲桂林,导游在大巴上是这么开讲的。 这是贵州人的自恋么? 一开始我这么认为,几天行走下来,...
    湄之阅读 1,745评论 1 3