Подтвердить что ты не робот

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?

Спасибо за любой намек.

4b9b3361

Ответ 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)