【问题标题】:NSNotificationCenter posting notifications doesn't workNSNotificationCenter 发布通知不起作用
【发布时间】:2021-12-07 07:41:49
【问题描述】:

我有一个函数,它使用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 类:

// 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(如第一个代码sn-p所示)会将实例添加为observer,并且HandleNotification是从WifiNetworkChangeCB块发布的wifi通知的接收者。

问题是,当我收到wifi事件时,调用了WifiNetworkChangeCB并执行了postNotificationName函数(已通过调试器验证),但HandleNotification没有收到通知。

我得到以下输出:

TestingNWPathMonitor
RegisterNotification!
Wifi Network change!!

而预期的输出是:

TestingNWPathMonitor
RegisterNotification!
Wifi Network change!!
Received notification: WifiNetworkChange

我已阅读通知中心的documentation 了解其用法。也提到了这个answer。 我还参考了我正在使用的函数的文档(在解释问题时将它们添加为超链接),一切似乎都很好。

但我显然遗漏了一些东西(因为它不起作用)。任何帮助将不胜感激。

【问题讨论】:

  • init NotificationReceiver 在哪里?难道是它被释放得太早了?
  • @Larme NotificationReceiver 的 init 在 TestNWPathMonitor 函数中调用(第一个代码 sn-p)。我还没有走到尽头,所以还没有移除观察者或停止路径监视器。
  • TestNWPathMonitor () 范围告诉我们,monitor 是全局的,NotificationReceiver *notification_receiver 不是。因此,当通知表明满意的工作时,notification_receiver 不再存在。您也可以通过在 main.m 中添加观察者来证明这个概念。 ps:在objc中methodnames以小写字母开头,当setter & getter违反“camelCased”methodname规则时,你会遇到麻烦。
  • @OlSen 哇,谢谢。这就是问题所在。在我将NotificationReceiver *notification_receiver 设为全局后,我能够获得预期的输出。将此作为答案,以便我可以将此帖子标记为“已解决”?
  • @OlSen 我来自 C++ 背景。由于 'NotificationReceiver *notification_receiver' 是一个指针,我认为它会超出函数的范围。谢谢。

标签: ios objective-c macos nsnotificationcenter nwpathmonitor


【解决方案1】:

原因:您的 C 函数 TestNWPathMonitor() 分配了 NotificationReceiver *notification_receiver,但是当您离开作用域时,创建的对象没有存储在任何地方。因此,使用 ARC 内存管理,当范围块离开时,对象将被释放,也就是它的堆栈再次“空”。

您的monitor 又名typedef NSObject&lt;OS_nw_path_monitor&gt; *nw_path_monitor_t; 似乎是一个全局变量,因此在离开范围后它仍然存在,这可能会让您产生误解,即 objc 分配也是如此,是的,也不是。如果 monitor 是一个局部变量,也会发生同样的情况。

调试:观察[NSNotificationCenter defaultCenter] 允许您在代码中几乎任何地方捕获通知,无论您在等待什么线程,它都是一个基于 NSString 的 API理由与它的所有优点和缺点。由于这种简单的方法,很难找到它不起作用的原因。但基本上在main.mAPPDelegate 中放置一个观察者应该总是告诉你它在发布方面是否正常工作,以确保你没有拼错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];
                                   }];

在上面的代码 sn-p 中看到[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&getter违反“camelCased”方法名规则时你会遇到麻烦,因为接口
@property NSObject *someName;变成了
-(NSObject*)someName; 作为 getter,-(void)setSomeName:(NSObject*)somename; 作为现代 objc 的 setter。这也说明了为什么我们使用较低的下划线来标​​记几乎所有属性对应的局部类变量。在这个给定的示例中,属性NSObject *someName 将有一个内部_someName 对应项。这里没有像在 oldschool objc 中那样深入了解更多关于类声明 @dynamic ...@synthesize ... 的知识,它们允许更详细地控制(内部)局部类变量名称。
为什么要为此烦恼? 您的NotificationReceiver *notification_receiver 可以覆盖具有相同名称的类属性,给您的印象是您所做的一切都是正确的,但仍然无法正常工作,因为声明仍然会使堆栈为空。因此,在方法/功能块中声明像 _notification_receiver = ... 这样的变量将非常清楚您的意思是其 @property NotificationReceiver *notification_receiver; 的内部对应物,而不是额外的局部变量。

【讨论】:

  • 谢谢。这很有用。非常感谢解释 ObjC 约定:)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-05
  • 2023-03-17
  • 2022-11-22
  • 2012-12-20
  • 2017-12-16
相关资源
最近更新 更多