0

我有一个函数,它使用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 事件并将通知发布到默认通知中心

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(如第一个代码片段所示)会将实例添加为观察者,而 HandleNotification 是从 WifiNetworkChangeCB 块发布的 wifi 通知的接收者。

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

我得到以下输出:

TestingNWPathMonitor
RegisterNotification!
Wifi Network change!!

而预期的输出是:

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

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

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

4

1 回答 1

1

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

您的monitorakatypedef NSObject<OS_nw_path_monitor> *nw_path_monitor_t;似乎是一个全球性的,因此在离开范围后它仍然存在,这可能会给您一种误解,即 objc 分配也是如此,是的,也不是。监视它是否是局部变量也会发生同样的情况。

调试:观察[NSNotificationCenter defaultCenter]允许您在代码中几乎任何地方捕获通知,无论您在等待什么线程,它是一个基于 NSString 的 API ,这是有充分理由的,具有所有优点和缺点。由于这种简单的方法,很难找到它不起作用的原因。但基本上放置一个观察者main.m或者APPDelegate应该总是告诉你它在发布方面是否正常工作,以确保你没有拼错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 & getter 违反“camelCased”方法名规则时,您将遇到麻烦,因为接口
@property NSObject *someName;变成了现代 objc
-(NSObject*)someName;的 getter 和-(void)setSomeName:(NSObject*)somename;setter。这也说明了为什么我们使用较低的下划线来标​​记几乎所有属性对应的局部类变量。在这个给定的示例中,属性NSObject *someName将具有内部_someName对应项。这里没有像在 oldschool objc 中那样深入了解,还有更多关于类声明的知识@dynamic ...@synthesize ...允许更详细地控制(内部)局部类变量名称。
为什么要为此烦恼?您的NotificationReceiver *notification_receiver可以覆盖具有相同名称的类属性,给您的印象是您所做的一切都是正确的,但仍然无法正常工作,因为该声明仍然会使堆栈为空。因此,像在方法/功能块中那样声明变量_notification_receiver = ...将非常清楚您的意思是它的内部对应物,@property NotificationReceiver *notification_receiver;而不是额外的局部变量。

于 2021-10-21T17:25:23.897 回答