Уведомления о размещении NSNotificationCenter не работают
У меня есть которая использует функция,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);
}
Я предоставил обратный вызов, который будет вызываться при изменении сетевых событий. В обратном вызове (как показано ниже) я ищу события Wi-Fi и отправляю уведомление в уведомлений по центрумолчанию .
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 является получателем уведомления Wi-Fi, отправленного из блока WifiNetworkChangeCB.
Проблема в том, что когда я получаю событие Wi-Fi, вызывается WifiNetworkChangeCB и функция выполняетсяpostNotificationName (проверено с помощью отладчика), но HandleNotification не получает уведомление.
Я получаю следующий результат:
TestingNWPathMonitor
RegisterNotification!
Wifi Network change!!
Принимая во внимание, что ожидаемый результат:
TestingNWPathMonitor
RegisterNotification!
Wifi Network change!!
Received notification: WifiNetworkChange
Я прочитал документацию центра уведомлений, чтобы понять его использование. Также сослались на этот ответ . Я также сослался на документацию по функциям, которые я использую (добавил их в виде гиперссылок при объяснении проблемы), все в порядке.
Но мне явно чего-то не хватает (так как это не сработало). Любая помощь будет оценена.
1 ответ
Причина: ваша функция C
TestNWPathMonitor()
выделяет, но созданный объект нигде не сохраняется, когда вы покидаете область видимости. Таким образом, при управлении памятью ARC объект будет освобожден, когда останется блок scopes, иначе его стек снова станет «пустым».
ваш
monitor
он же
typedef NSObject<OS_nw_path_monitor> *nw_path_monitor_t;
кажется глобальным, поэтому он все еще будет существовать после выхода из области видимости, что дает вам, возможно, неправильное представление, что то же самое было бы в случае выделения objc, ну да и нет. То же самое произошло бы и для мониторинга, если бы это была локальная переменная.
Отладка: наблюдение
[NSNotificationCenter defaultCenter]
позволяет получать уведомления практически в любом месте вашего кода, независимо от того, какой поток вы их ждете, это API на основе NSString по уважительной причине со всеми его плюсами и минусами. Из-за такого простого подхода может быть трудно понять, почему он не работает. Но в основном размещение наблюдателя в
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
заставляет вас позже обернуть материал пользовательского интерфейса в
dispatch_async(dispatch_get_main_queue(), ^{ /* UI code block */ }); // or
dispatch_sync(dispatch_get_main_queue(), ^{ /* UI code block */ });
что вам не нужно делать, когда вы сказали наблюдателю уведомлений, где выполнить блок уведомлений при получении.
Ps: в objc мы начинаем имена методов с маленьких букв, вы столкнетесь с проблемами, когда установщики и геттеры нарушат правило имени метода "camelCased", потому что интерфейс
@property NSObject *someName;
становится
-(NSObject*)someName;
как получатель и
-(void)setSomeName:(NSObject*)somename;
как сеттер с современным objc. Это также говорит о том, почему мы используем нижнее подчеркивание для обозначения переменных локального класса, которые являются аналогами почти любого свойства .. в данном примере свойство
NSObject *someName
имел бы внутренний
_someName
аналог. Не будем углубляться здесь, так как в oldschool objc нужно больше знать об объявлениях классов
@dynamic ...
&
@synthesize ...
которые позволяют более детально контролировать (внутреннее) имя локальной переменной класса.
Зачем об этом беспокоиться? Ваш
NotificationReceiver *notification_receiver
может переопределить свойство класса с тем же именем, создавая впечатление, что вы все сделали правильно, но все еще не работает, так как объявление все равно оставит стек пустым. Итак, объявив переменную как
_notification_receiver = ...
в блоке методов / функций было бы очень ясно, что вы имели в виду внутренний аналог его
@property NotificationReceiver *notification_receiver;
а не дополнительная локальная переменная.