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.mAPPDelegate 中放置一个 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; 的内部对应项,而不是额外的局部变量。