參考文檔: Notification Programming Topics
NSNotificationCenter
- 用于單個進程內(nèi)的通知發(fā)送
-
postNotificationName:object:
和postNotificationName:object:userInfo:
的調(diào)用為同步方式,該方法會等待通知被發(fā)送和處理后返回其屏。 - 通知注冊的handler是在發(fā)送通知的線程中被調(diào)用喇勋,主線程注冊了通知,子線程發(fā)送了通知偎行,通知的處理就是在子線程中川背。如果想在主線程中同步處理該通知贰拿,可以通過
mach port
, 將通知轉(zhuǎn)發(fā)給主線程。
NSNotificationQueue
NSNotificationQueue objects, or simply, notification queues, act as buffers for notification centers (instances of NSNotificationCenter). The NSNotificationQueue class contributes two important features to the Foundation Kit’s notification mechanism: the coalescing of notifications and asynchronous posting.
Using the NSNotificationCenter’s postNotification: method and its variants, you can post a notification to a notification center. However, the invocation of the method is synchronous: before the posting object can resume its thread of execution, it must wait until the notification center dispatches the notification to all observers and returns. A notification queue, on the other hand, maintains notifications (instances of NSNotification) generally in a First In First Out (FIFO) order. When a notification rises to the front of the queue, the queue posts it to the notification center, which in turn dispatches the notification to all objects registered as observers.
Every thread has a default notification queue, which is associated with the default notification center for the process. You can create your own notification queues and have multiple queues per center and thread.
- 每條線程都有一個默認的通知隊列熄云, 隊列的任務(wù)先入先出
- 通過通知隊列可以異步發(fā)送通知膨更,通過
NSNotificationCenter
只能同步發(fā)送通知 - 通知隊列允許通知折疊,多條通知折疊為一條發(fā)送缴允,可以指定折疊的策略荚守, 例如: 按通知名字折疊
- 需要關(guān)聯(lián)runloop,指定要運行的runloop mode
- 因為指定了runloop练般,指定通知發(fā)送的時機
- NSPostASAP
- NSPostWhenIdle
- NSPostNow
調(diào)用:
NSNotificationQueue *queue = [NSNotificationQueue defaultQueue];
[queue enqueueNotification ...]
NSDistributedNotificationCenter
Each process has a default distributed notification center that you access with the NSDistributedNotificationCenter +defaultCenter
class method. This distributed notification center handles notifications that can be sent between processes on a single machine. For communication between processes on different machines, use distributed objects (see Distributed Objects Programming Topics).
Posting a distributed notification is an expensive operation. The notification gets sent to a systemwide server that then distributes it to all the processes that have objects registered for distributed notifications. The latency between posting the notification and the notification’s arrival in another process is unbounded. In fact, if too many notifications are being posted and the server’s queue fills up, notifications can be dropped.
Distributed notifications are delivered via a process’s run loop. A process must be running a run loop in one of the “common” modes, such as NSDefaultRunLoopMode, to receive a distributed notification. If the receiving process is multithreaded, do not depend on the notification arriving on the main thread. The notification is usually delivered to the main thread’s run loop, but other threads could also receive the notification.
Whereas a regular notification center allows any object to be observed, a distributed notification center is restricted to observing a string object. Because the posting object and the observer may be in different processes, notifications cannot contain pointers to arbitrary objects. Therefore, a distributed notification center requires notifications to use a string as the notification object. Notification matching is done based on this string, rather than an object pointer.
- 提供跨進程通知的能力, iOS用不了矗漾, iOS需要使用
CFNotificationCenterGetDarwinNotifyCenter
- 當(dāng)通知隊列滿的時候,后面到來的通知會被丟棄
- 需要關(guān)聯(lián)runloop薄料, 一般的通知會發(fā)布到主線程中敞贡,但是子線程也可以收到通知
- 通知的object信息,只能是string摄职, 因為跨進程通知誊役,傳遞對象指針沒有意義
- 可以定制策略和發(fā)送時機
- (void)addObserver:(id)observer selector:(SEL)selector name:(NSNotificationName)name object:(NSString *)object suspensionBehavior:(NSNotificationSuspensionBehavior)suspensionBehavior;
- (void)postNotificationName:(NSNotificationName)name object:(NSString *)object userInfo:(NSDictionary *)userInfo options:(NSDistributedNotificationOptions)options;
CFNotificationCenterGetDarwinNotifyCenter
支持 iOS、macOS琳钉, 提供跨進程通知的能力
swift 中如何使用, 參考: Swift - 正確使用CFNotificationCenterAddObserver 回調(diào)
//發(fā)送通知
sendNotificationForMessageWithIdentifier(identifier: "broadcastStarted")
func sendNotificationForMessageWithIdentifier(identifier : String) {
let center : CFNotificationCenter = CFNotificationCenterGetDarwinNotifyCenter()
let identifierRef : CFNotificationName = CFNotificationName(identifier as CFString)
CFNotificationCenterPostNotification(center, identifierRef, nil, nil, true)
}
///通知回調(diào)
func callback(_ name : String) {
print("received notification: \(name)")
}
///通知注冊
func registerObserver() {
let observer = UnsafeRawPointer(Unmanaged.passUnretained(self).toOpaque())
CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), observer, { (_, observer, name, _, _) -> Void in
if let observer = observer, let name = name {
// Extract pointer to `self` from void pointer:
let mySelf = Unmanaged<OTCAppealVC>.fromOpaque(observer).takeUnretainedValue()
// Call instance method:
mySelf.callback(name.rawValue as String)
}
}, "broadcastFinished" as CFString, nil,.deliverImmediately)
}
//通知移除
deinit {
let observer = UnsafeRawPointer(Unmanaged.passUnretained(self).toOpaque())
let cfName: CFNotificationName = CFNotificationName("broadcastFinished" as CFString)
CFNotificationCenterRemoveObserver(CFNotificationCenterGetDarwinNotifyCenter(), observer, cfName, nil)
}
NSMachPort 轉(zhuǎn)發(fā)通知到特定線程
例如在主線程監(jiān)聽,在子線程中發(fā)送通知蛛倦,希望通知仍然在主線程中處理
#import "ViewController.h"
@interface ViewController ()<NSMachPortDelegate>
@property(nonatomic, strong) NSMutableArray *notifications;
@property (nonatomic, strong) NSThread *notificationThread;
@property (nonatomic, strong) NSLock *notificationLock;
@property (nonatomic, strong) NSMachPort *notificationPort;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.notifications = [[NSMutableArray alloc] init];
self.notificationThread = [NSThread mainThread];
self.notificationLock = [[NSLock alloc] init];
self.notificationPort = [[NSMachPort alloc] init];
[self.notificationPort setDelegate:self];
[[NSRunLoop currentRunLoop] addPort:self.notificationPort
forMode:NSRunLoopCommonModes];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(processNotification:)
name:@"NotificationName"
object:nil];
for (int i = 0; i< 10; i++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:@"NotificationName" object:self];
});
}
}
- (void) handleMachMessage:(void *)msg {
[self.notificationLock lock];
while ([self.notifications count]) {
NSNotification *notification = [self.notifications objectAtIndex:0];
[self.notifications removeObjectAtIndex:0];
[self.notificationLock unlock];
[self processNotification:notification];
[self.notificationLock lock];
};
[self.notificationLock unlock];
}
- (void)processNotification:(NSNotification *)notification {
if ([NSThread currentThread] != self.notificationThread) {
// Forward the notification to the correct thread.
[self.notificationLock lock];
[self.notifications addObject:notification];
[self.notificationLock unlock];
[self.notificationPort sendBeforeDate:[NSDate date]
components:nil
from:nil
reserved:0];
NSLog(@"notification receive in thread: %@", [NSThread currentThread]);
}
else {
// Process the notification here;
NSLog(@"notification handle in thread: %@", [NSThread currentThread]);
}
}
@end