场景
-
在
macOS下进行Cocoa开发时,我们偶尔会需要监听窗口的大小改变的通知,以便能控制子窗口或者NSView改变大小. 比如NSWindowDidResizeNotification通知. 那么我们应该如何监听父窗口的大小改变通知? -
如果我们使用
NSNotificationCenter进行postNotificationName消息, 这个消息发送到接受者是异步还是同步的?
说明
-
Cocoa包括两种类型的通知中心.NSNotificationCenter管理同进程的通知, 而NSDistributedNotificationCenter管理在同一个系统里的跨进程的通知. -
这里我们开发界面程序,在同一个进程里的通知都是通过
NSNotificationCenter管理. 每个进程都有一个默认的通知中心,这个通知中心你可以通过[NSNotificationCenter defaultCenter]类方法来获取. 这个通知中心处理同一个进程里通知. 这个通知中心的设计模式就是我们常听说的观察者模式或者说是发布订阅模式. 我在这篇文章里有说观察者模式在项目中实际使用例子2 -
NSNotificationCenter使用通知有以下注意点:- 通知中心发送通知给观察者是同步的. 即当
postNotificationName发布一个通知调用,控制器(通知中心)不会返回,直到所有的观察者已经接收并处理这些消息. 如果想发送异步通知,那么需要使用一个通知队列NSNotificationQueue, 这里不做细讲. - 在多线程程序, 通知往往可以在某个线程发出,而和观察者注册通知处理方法的线程不一样是可以的。
- 每一个线程都有
默认的通知队列(notification queue),它关联着进程的通知中心。这个队列用来维护通知实例NSNotification。 这个通知队列遵守FIFO顺序,依次把通知转发给通知中心,而通知中心把这个通知发送给注册的观察者. - 如果工作线程(非界面线程) 发出一个通知给通知中心,而观察者的处理方法却调用了界面绘制相关操作,比如设置文本值,那么可能会导致程序崩溃,因为界面元素需又界面线程操作,所以接收的通知是在界面操作,那么这个
postNotificationName必须要在界面线程调用(即主线程).
- 通知中心发送通知给观察者是同步的. 即当
-
使用完通知要把它从通知中心移除,不然这个通知会一直存在造成以下问题:
- 无效的通知中心观察者存在内存泄漏.
- 外部如果发送未被移除的通知, 而观察者已经销毁,那么程序会崩溃.
-
观察者必须是一个
Object-C对象, 以下addObserver方法必须有值,不能是nil.
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receiveMessage:) name:kBookOpenNotification object:nil];
例子
- 以下是一个命令行程序演示了
NSNotificationCenter最常用的同步发送通知.
//
// main.m
// test-notification
//
// Created by sai on 5/2/20.
// Copyright (c) 2020 tobey. All rights reserved.
//
#import <Foundation/Foundation.h>
static NSString* kBookCancelNotification = @"BookCancelNotification";
static NSString* kBookOpenNotification = @"BookOpenNotification";
static BOOL shouldKeepRunning = NO;
@interface Book: NSObject
@end
@implementation Book
-(void)receiveMessage:(NSNotification*)data
{
NSDictionary* dic = data.object;
NSLog(@"%@ data is %@",data.name,dic);
}
-(id)init
{
NSLog(@"Book init");
self = [super init];
// object 为nil时,接收任何位置发的通知.
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receiveMessage:) name:kBookCancelNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receiveMessage:) name:kBookOpenNotification object:nil];
return self;
}
-(void)dealloc
{
NSLog(@"Book dealloc");
// 使用完通知要把它从通知中心移除,不然这个通知会一直存在造成以下问题:
// 1.无效的通知中心观察者存在内存泄漏.
// 2.外部如果发送 kBookCancelNotification 消息,而BOOK已经销毁,那么程序会崩溃.
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
@end
void testNotification()
{
NSLog(@"================ testNotification BEGIN ================");
@autoreleasepool {
Book* book = [Book new];
NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys:@"https://blog.youkuaiyun.com/infoworld",@"blog",nil];
NSLog(@"== postNotificationName kBookCancelNotification ==");
[[NSNotificationCenter defaultCenter] postNotificationName:kBookCancelNotification object:dict userInfo:nil];
NSDictionary* dict1 = [NSDictionary dictionaryWithObjectsAndKeys:@"Tobey",@"name",nil];
NSLog(@"== postNotificationName kBookOpenNotification ==");
[[NSNotificationCenter defaultCenter] postNotificationName:kBookOpenNotification object:dict1 userInfo:nil];
}
// 1.如果Book dealloc 里没有调用 removeObserver,那么以下代码调用即会崩溃.
// 2.如果Book dealloc 里调用了removeObserver,那么消息中心没有对应的处理观察者,会丢弃这个通知.
NSDictionary* dict2 = [NSDictionary dictionaryWithObjectsAndKeys:@"Tobey",@"name",nil];
[[NSNotificationCenter defaultCenter] postNotificationName:kBookOpenNotification object:dict2 userInfo:nil];
NSLog(@"================ testNotification END ================");
}
int main(int argc, const char * argv[])
{
@autoreleasepool {
// insert code here...
NSLog(@"Hello, World!");
testNotification();
dispatch_async(dispatch_get_main_queue(),^(){
shouldKeepRunning = NO;
});
NSRunLoop *theRL = [NSRunLoop currentRunLoop];
while (shouldKeepRunning && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
}
return 0;
}
输出
2020-05-02 23:48:52.950 test-notification[896:303] Hello, World!
2020-05-02 23:48:52.952 test-notification[896:303] ================ testNotification BEGIN ================
2020-05-02 23:48:52.953 test-notification[896:303] Book init
2020-05-02 23:48:52.953 test-notification[896:303] == postNotificationName kBookCancelNotification ==
2020-05-02 23:48:52.954 test-notification[896:303] BookCancelNotification data is {
blog = "https://blog.youkuaiyun.com/infoworld";
}
2020-05-02 23:48:52.954 test-notification[896:303] == postNotificationName kBookOpenNotification ==
2020-05-02 23:48:52.955 test-notification[896:303] BookOpenNotification data is {
name = Tobey;
}
2020-05-02 23:48:52.955 test-notification[896:303] Book dealloc
2020-05-02 23:48:52.956 test-notification[896:303] ================ testNotification END ================
本文深入探讨了Cocoa中NSNotification的使用,包括NSNotification的同步发送特性、NSNotificationCenter的管理方式及多线程处理注意事项,提供了实际代码示例。
3243

被折叠的 条评论
为什么被折叠?



