NSNotificationCenter 发布通知不起作用
NSNotificationCenter posting notifications doesn't work
我有一个使用 nw_path_monitor_t 注册网络事件的函数。
// Entry point.
// Will be called from AppDelegate when app starts up
void TestNWPathMonitor () {
PrintToFile("TestingNWPathMonitor\n");
NotificationReceiver *notification_receiver = [[NotificationReceiver alloc] init];
// Set up the notification receiver to listen for wifi notification
[notification_receiver RegisterNotification];
monitor = nw_path_monitor_create ();
nw_path_monitor_set_update_handler (monitor, WifiNetworkChangeCB);
nw_path_monitor_start(monitor);
}
我已经提供了回调,当网络事件发生变化时会调用它。在回调中(如下所示),我正在寻找 wifi 事件并将通知发布到 default notification center.
nw_path_monitor_update_handler_t WifiNetworkChangeCB = ^ (nw_path_t path) {
PrintToFile("Wifi Network change!!\n");
nw_path_status_t status = nw_path_get_status (path);
if (nw_path_uses_interface_type (path, nw_interface_type_wifi)) {
if (status == nw_path_status_satisfied) {
PrintToFile("nw_path_status_satisfied\n");
[[NSNotificationCenter defaultCenter] postNotificationName:@"WifiNetworkChange" object:nil];
} else {
PrintToFile("!(nw_path_status_satisfied)\n");
}
}
};
这是 NotificationReceiver class:
// NotificationReceiver.h
#include <Foundation/Foundation.h>
@interface NotificationReceiver : NSObject
- (void) HandleNotification : (NSNotification *) pNotification;
- (void) RegisterNotification ;
@end
// NotificaitonReceiver.m
@implementation NotificationReceiver
- (void) HandleNotification : (NSNotification *) pNotification {
PrintToFile([[NSString stringWithFormat:@"Received notification: %@\n", pNotification.name] UTF8String]);
}
- (void) RegisterNotification {
PrintToFile("RegisterNotification!\n");
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(HandleNotification:) name:@"WifiNetworkChange" object:nil];
}
@end
RegisterNotification,在开头调用(如第一个代码片段所示)会将实例添加为 observer 并且 HandleNotification 是从 WifiNetworkChangeCB 块发布的 wifi 通知的接收者。
问题是,当我收到 wifi 事件时,会调用 WifiNetworkChangeCB 并执行 postNotificationName 函数(已通过调试器验证),但 HandleNotification 没有收到通知。
我得到以下输出:
TestingNWPathMonitor
RegisterNotification!
Wifi Network change!!
然而,预期的输出是:
TestingNWPathMonitor
RegisterNotification!
Wifi Network change!!
Received notification: WifiNetworkChange
我已阅读 documentation of the notification center to understand its usage. Have also referred this answer。
我还参考了我正在使用的函数的文档(在解释问题时将它们添加为超链接),一切似乎都很好。
但我显然遗漏了一些东西(因为它不起作用)。任何帮助将不胜感激。
原因: 你的 C 函数 TestNWPathMonitor()
分配了 NotificationReceiver *notification_receiver
但创建的对象在你离开范围时没有存储。因此,使用 ARC 内存管理,对象将在作用域块离开时被释放,也就是它的堆栈再次“空”。
你的 monitor
又名 typedef NSObject<OS_nw_path_monitor> *nw_path_monitor_t;
似乎是一个全局的,所以它在离开范围后仍然存在,这给你可能的误解,即 objc 分配也是如此,好吧,是的,不是.如果 monitor 是局部变量,也会发生同样的情况。
调试: 观察 [NSNotificationCenter defaultCenter]
允许您在代码中几乎任何地方捕获通知,无论您在哪个线程等待它们,它都是 基于 NSString API 的所有优点和缺点都有充分的理由。由于这种简单的方法,很难找到它不起作用的原因。但是基本上在 main.m
或 APPDelegate
中放置一个 Observer 应该总是告诉你它是否在发布端正常工作,以确保你没有拼错 NotificationName
。为了避免后一种情况,我们经常声明
extern NotificationName const kSomeNiceNotification;
// in .h && the following in .m
NotificationName const kSomeNiceNotification = @"kSomeNiceNotification";
并使用这个全局键作为名称。
提示:您还可以创建一个单次通知,该通知将在收到时被触发并自行销毁,同时您在这样做时必须考虑其他含义。像这样(来自 Xcode 文档)..
NSNotificationCenter * __weak center = [NSNotificationCenter defaultCenter];
id __block token = [center addObserverForName:@"OneTimeNotification"
object:nil
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification *note) {
NSLog(@"Received the notification!");
[center removeObserver:token];
}];
看到上面代码片段中的[NSOperationQueue mainQueue]
了吗?
您可以在那里传递 nil
但通知块将在发送通知的线程上执行。当在 UI 代码中使用时,这通常是通知的用例,这很重要,因为 UI 任务需要在主线程上执行,其中传递 nil
会强制您稍后包装 UI 东西在
dispatch_async(dispatch_get_main_queue(), ^{ /* UI code block */ }); // or
dispatch_sync(dispatch_get_main_queue(), ^{ /* UI code block */ });
当您告诉通知观察者在收到通知时在哪里执行通知块时,您不需要这样做。
Ps: 在 objc 中,我们以小写字母开头的方法名称,当 setter & [=75= 时,你会 运行 遇到麻烦]s 违反了“驼峰式”方法名规则,因为接口
@property NSObject *someName;
变为
-(NSObject*)someName;
为 getter 和 -(void)setSomeName:(NSObject*)somename;
为 setter 与现代对象。这也说明了为什么我们使用较低的下划线来标记局部 class 变量,这些变量几乎是任何 属性 的对应物。在这个给定的例子中 属性 NSObject *someName
会有一个内部 _someName
对应方。在这里不深入,因为在 oldschool objc 中有更多关于 class 声明 @dynamic ...
和 @synthesize ...
的知识,它们允许更详细地控制(内部)本地 class 变量名.
为什么要为此烦恼? 你的 NotificationReceiver *notification_receiver
可以覆盖同名的 class 属性 给你的印象是你做的一切都是正确的,但仍然是不工作,因为声明仍然会使堆栈为空。因此,在 methods/function 块中声明 _notification_receiver = ...
之类的变量会非常清楚,您的意思是其 @property NotificationReceiver *notification_receiver;
的内部对应项,而不是额外的局部变量。
我有一个使用 nw_path_monitor_t 注册网络事件的函数。
// Entry point.
// Will be called from AppDelegate when app starts up
void TestNWPathMonitor () {
PrintToFile("TestingNWPathMonitor\n");
NotificationReceiver *notification_receiver = [[NotificationReceiver alloc] init];
// Set up the notification receiver to listen for wifi notification
[notification_receiver RegisterNotification];
monitor = nw_path_monitor_create ();
nw_path_monitor_set_update_handler (monitor, WifiNetworkChangeCB);
nw_path_monitor_start(monitor);
}
我已经提供了回调,当网络事件发生变化时会调用它。在回调中(如下所示),我正在寻找 wifi 事件并将通知发布到 default notification center.
nw_path_monitor_update_handler_t WifiNetworkChangeCB = ^ (nw_path_t path) {
PrintToFile("Wifi Network change!!\n");
nw_path_status_t status = nw_path_get_status (path);
if (nw_path_uses_interface_type (path, nw_interface_type_wifi)) {
if (status == nw_path_status_satisfied) {
PrintToFile("nw_path_status_satisfied\n");
[[NSNotificationCenter defaultCenter] postNotificationName:@"WifiNetworkChange" object:nil];
} else {
PrintToFile("!(nw_path_status_satisfied)\n");
}
}
};
这是 NotificationReceiver class:
// NotificationReceiver.h
#include <Foundation/Foundation.h>
@interface NotificationReceiver : NSObject
- (void) HandleNotification : (NSNotification *) pNotification;
- (void) RegisterNotification ;
@end
// NotificaitonReceiver.m
@implementation NotificationReceiver
- (void) HandleNotification : (NSNotification *) pNotification {
PrintToFile([[NSString stringWithFormat:@"Received notification: %@\n", pNotification.name] UTF8String]);
}
- (void) RegisterNotification {
PrintToFile("RegisterNotification!\n");
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(HandleNotification:) name:@"WifiNetworkChange" object:nil];
}
@end
RegisterNotification,在开头调用(如第一个代码片段所示)会将实例添加为 observer 并且 HandleNotification 是从 WifiNetworkChangeCB 块发布的 wifi 通知的接收者。
问题是,当我收到 wifi 事件时,会调用 WifiNetworkChangeCB 并执行 postNotificationName 函数(已通过调试器验证),但 HandleNotification 没有收到通知。
我得到以下输出:
TestingNWPathMonitor
RegisterNotification!
Wifi Network change!!
然而,预期的输出是:
TestingNWPathMonitor
RegisterNotification!
Wifi Network change!!
Received notification: WifiNetworkChange
我已阅读 documentation of the notification center to understand its usage. Have also referred this answer。 我还参考了我正在使用的函数的文档(在解释问题时将它们添加为超链接),一切似乎都很好。
但我显然遗漏了一些东西(因为它不起作用)。任何帮助将不胜感激。
原因: 你的 C 函数 TestNWPathMonitor()
分配了 NotificationReceiver *notification_receiver
但创建的对象在你离开范围时没有存储。因此,使用 ARC 内存管理,对象将在作用域块离开时被释放,也就是它的堆栈再次“空”。
你的 monitor
又名 typedef NSObject<OS_nw_path_monitor> *nw_path_monitor_t;
似乎是一个全局的,所以它在离开范围后仍然存在,这给你可能的误解,即 objc 分配也是如此,好吧,是的,不是.如果 monitor 是局部变量,也会发生同样的情况。
调试: 观察 [NSNotificationCenter defaultCenter]
允许您在代码中几乎任何地方捕获通知,无论您在哪个线程等待它们,它都是 基于 NSString API 的所有优点和缺点都有充分的理由。由于这种简单的方法,很难找到它不起作用的原因。但是基本上在 main.m
或 APPDelegate
中放置一个 Observer 应该总是告诉你它是否在发布端正常工作,以确保你没有拼错 NotificationName
。为了避免后一种情况,我们经常声明
extern NotificationName const kSomeNiceNotification;
// in .h && the following in .m
NotificationName const kSomeNiceNotification = @"kSomeNiceNotification";
并使用这个全局键作为名称。
提示:您还可以创建一个单次通知,该通知将在收到时被触发并自行销毁,同时您在这样做时必须考虑其他含义。像这样(来自 Xcode 文档)..
NSNotificationCenter * __weak center = [NSNotificationCenter defaultCenter];
id __block token = [center addObserverForName:@"OneTimeNotification"
object:nil
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification *note) {
NSLog(@"Received the notification!");
[center removeObserver:token];
}];
看到上面代码片段中的[NSOperationQueue mainQueue]
了吗?
您可以在那里传递 nil
但通知块将在发送通知的线程上执行。当在 UI 代码中使用时,这通常是通知的用例,这很重要,因为 UI 任务需要在主线程上执行,其中传递 nil
会强制您稍后包装 UI 东西在
dispatch_async(dispatch_get_main_queue(), ^{ /* UI code block */ }); // or
dispatch_sync(dispatch_get_main_queue(), ^{ /* UI code block */ });
当您告诉通知观察者在收到通知时在哪里执行通知块时,您不需要这样做。
Ps: 在 objc 中,我们以小写字母开头的方法名称,当 setter & [=75= 时,你会 运行 遇到麻烦]s 违反了“驼峰式”方法名规则,因为接口 @property NSObject *someName;
变为 -(NSObject*)someName;
为 getter 和 -(void)setSomeName:(NSObject*)somename;
为 setter 与现代对象。这也说明了为什么我们使用较低的下划线来标记局部 class 变量,这些变量几乎是任何 属性 的对应物。在这个给定的例子中 属性 NSObject *someName
会有一个内部 _someName
对应方。在这里不深入,因为在 oldschool objc 中有更多关于 class 声明 @dynamic ...
和 @synthesize ...
的知识,它们允许更详细地控制(内部)本地 class 变量名.
为什么要为此烦恼? 你的 NotificationReceiver *notification_receiver
可以覆盖同名的 class 属性 给你的印象是你做的一切都是正确的,但仍然是不工作,因为声明仍然会使堆栈为空。因此,在 methods/function 块中声明 _notification_receiver = ...
之类的变量会非常清楚,您的意思是其 @property NotificationReceiver *notification_receiver;
的内部对应项,而不是额外的局部变量。