NSNotificationCenter захватывает и отслеживает все NSNotifications
Для лучшего понимания того, что происходит "под капотом", я хотел бы сделать полный след любых уведомлений, происходящих в моем приложении.
Наивный, как я, первое, что я пробовал, регистрировалось следующим образом:
Где-то в моем приложении:
{
[...]
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(traceNotifications:) name:nil object:nil];
[...]
}
- (void)traceNotifications:(NSNotification *)notification
{
NSLog(@"received notification %@", [notification name]);
}
Я действительно получаю несколько уведомлений таким образом. Но в какой-то момент приложение рушится. Трассировка стека показывает, что он сбой с EXC_BAD_ACCESS в реализации classClass, который, по моему опыту, указывает на то, что что-то вызывается после его освобождения. Однако мой наблюдательный объект все еще жив, его деллалокатор еще не был назван (пока).
Затем я попытался установить точку останова в направлении -[NSNotificationCenter postNotification:]
, а затем запустить po {NSNotification *}($ebp+16)
внутри консоли gdb всякий раз, когда моя точка останова попадает в ловушку. Это показало несколько уведомлений, но не все, что я ожидаю/надеюсь. Например, мое приложение правильно обрабатывает изменения ориентации, но я не вижу, чтобы какие-либо уведомления были захвачены при переориентации устройства (в симуляторе).
Что мне не хватает?
Есть ли способ (например, инструмент) для надежного наблюдения за NSNotificationCenter?
Спасибо за любой намек.
Ответы
Ответ 1
В целях отладки я считаю, что точка останова на самом деле лучше, чем добавление кода в проект. Тем не менее, @Till Solution, похоже, не работает для меня. Я нашел другое решение онлайн и немного его подправил.
Символическая точка останова
- Символ:
-[NSNotificationCenter postNotificationName:object:userInfo:]
- Состояние:
((NSRange)[$arg3 rangeOfString:@"^(_|NS|UI)" options:1024]).length == 0
- Действие: команда отладчика
po $arg3
- Автоматически продолжить после оценки действий
Примечания:
- Это условие запрещает отображение любых уведомлений, начинающихся с
_
, NS
или UI
.
1024
относится к NSRegularExpressionSearch
, который, по-видимому, недоступен для этой точки останова.
- Я использую
.length == 0
вместо .location == NSNotFound
, потому что NSNotFound
, кажется, оценивает другое значение (-1
или (NSUInteger)18446744073709551615
), чем возвращаемое значение в этой точке останова (9223372036854775807
).
Ответ 2
Единственное решение, с которым я работал, - это использовать точки останова.
Я добавил точку останова в __CFXNotificationPost_old
(CoreFoundation) и в комплекте с командой Debugger po {NSNotification *}($ebp+12)
. Все это прекрасно выполнимо в графическом интерфейсе Xcode:
- нажмите "Запустить" в меню приложения Xcode (вверху экрана).
- выберите "Отладчик"
- в окне отладчика нажмите "Show-Breakpoints"
- щелкните по строке "Enter Symbol-Name" и введите "__CFXNotificationPost_old"
- нажмите "+" на правой стороне.
- выберите "Debugger Command" в раскрывающемся списке
- введите "po {NSNotification *} ($ ebp + 12)
- (вы также можете активировать ведение журнала, установив флажок "Журнал" внизу).
- запустите приложение в сеансе симулятора-отладки из Xcode
Приложение прекратит свое выполнение всякий раз, когда NSNotification отправляется и отображает его в консоли gdb.
Я попытался создать точку трассировки в gdb, но не смог, потому что действия трассировки в Xcode gdb кажутся ошибками - или, может быть, я слишком тупой, чтобы заставить их работать.
Я также попытался создать собственный инструмент Dtrace script, но не смог, так как мой Dtrace Karate просто не достаточно силен.
Если вам удастся заставить любой из последних вариантов работать, пожалуйста, перейдите к ним и отправьте их в качестве альтернативного ответа - я буду продвигать и отмечать их как привилегированные.
UPDATE
После этого вопроса я нашел правильный способ улавливания всех уведомлений на уровне CoreFoundation.
Вот как это можно сделать:
void MyCallBack (CFNotificationCenterRef center,
void *observer,
CFStringRef name,
const void *object,
CFDictionaryRef userInfo)
{
NSLog(@"name: %@", name);
NSLog(@"userinfo: %@", userInfo);
}
CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(),
NULL,
MyCallBack,
NULL,
NULL,
CFNotificationSuspensionBehaviorDeliverImmediately);
На самом деле мне немного стыдно, что я раньше не смотрел ближе к интерфейсу CoreFoundation.
Ответ 3
Привет, пока я часто использую уведомления, и у меня были некоторые серьезные проблемы с их отладкой. Недавно я выпустил приложение под названием Spark Inspector (http://sparkinspector.com/), которое немного облегчает этот процесс. Вы добавляете фреймворк в свое приложение, и он изменит NSNotificationCenter, чтобы вы могли видеть таблицу всех уведомлений, отправленных и полученных в нашем приложении, со стеками трассировок, куда они были отправлены, и списком всех методов, которые их наблюдали. Я знаю это примерно на три года позже, но это может помочь!
![enter image description here]()
Ответ 4
Я знаю, что поставленный вопрос старый, но я решил, что ответю пару строк кода.
Вы можете просмотреть все уведомления, опубликованные во время работы вашего приложения через этот блок:
[[NSNotificationCenter defaultCenter] addObserverForName:nil
object:nil
queue:nil
usingBlock:^(NSNotification *notification) {
NSLog(@"%@", notification.name);
}];
Добавьте это в метод viewWillAppear соответствующего контроллера вида. (Конечно, вы должны удалить это из своего кода при подготовке своего приложения для любого вида распространения.)
Кроме того, не забудьте добавить это:
[[NSNotificationCenter defaultCenter] removeObserver:self];
К вашему выбранному диспетчеру просмотра соответствующий метод viewWillDisappear.
UPDATE:
Тот же ответ, но в Swift:
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
NSNotificationCenter.defaultCenter().addObserverForName(nil,
object: nil,
queue: nil) {
note in
print(note.name + "\r\n")
}
}
override func viewWillDisappear(animated: Bool) {
NSNotificationCenter.defaultCenter().removeObserver(self)
super.viewWillDisappear(animated)
}
Ответ 5
@До решения в Swift 5.0 & Xcode 11:
CFNotificationCenterAddObserver(
CFNotificationCenterGetLocalCenter(),
nil,
{ (notificationCenter, _, notificationName, _, dictionary) in
print(notificationName)
print(dictionary)
}, nil, nil, .deliverImmediately)