NSNotificationCenter, отслеживающий и отслеживающий все NSNotifications - PullRequest
32 голосов
/ 16 сентября 2010

Для лучшего понимания того, что происходит «под капотом», я хотел бы сделать полный след любых уведомлений, происходящих в моем приложении.

Наивный, как я, первое, что я попробовал, было зарегистрироваться так:

Где-то в моем приложении:

{
    [...]
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(traceNotifications:) name:nil object:nil];
    [...]
}

- (void)traceNotifications:(NSNotification *)notification
{
    NSLog(@"received notification %@", [notification name]);
}

Я действительно получаю несколько уведомлений, которыепуть.Но в какой-то момент приложение вылетает.Трассировка стека показывает, что происходит сбой с EXC_BAD_ACCESS в реализовать реализацией класса реализаций, что, по моему опыту, указывает на то, что что-то вызывается после освобождения.Мой объект наблюдения, тем не менее, все еще жив, его деаллокатор не был вызван (пока).

Затем я попытался установить точку останова на -[NSNotificationCenter postNotification:] и затем запускать po {NSNotification *}($ebp+16) в консоли gdb всякий раз, когда моя точка остановав ловушке.Это выявило несколько уведомлений, но не все, что я ожидаю / надеюсь на.Например, мое приложение правильно обрабатывает изменения ориентации, но я не вижу никаких уведомлений, перехватываемых при переориентации устройства (в симуляторе).

Чего мне не хватает?Есть ли способ (например, инструмент) для надежного наблюдения NSNotificationCenter?

Спасибо за любую подсказку.

Ответы [ 4 ]

57 голосов
/ 17 сентября 2010

Единственное решение, которое я получил, - использование точек останова.

Я добавил точку останова на __CFXNotificationPost_old (CoreFoundation) и связал ее с командой отладчика po {NSNotification *}($ebp+12). Все это хорошо выполнимо в графическом интерфейсе Xcode:

  • нажмите «Выполнить» в меню приложения Xcode (вверху экрана)
  • выберите «Отладчик»
  • в окне отладчика нажмите «Показать точки останова»
  • нажмите на строку «Ввести имя символа» и введите «__CFXNotificationPost_old»
  • нажмите на "+" на самой правой стороне
  • выберите «Команда отладчика» в этом выпадающем списке
  • введите "po {NSNotification *} ($ ebp + 12)
  • (вы также можете активировать ведение журнала, установив флажок «Журнал» внизу)
  • запустить ваше приложение в сеансе отладки симулятора из Xcode

Приложение останавливает свое выполнение при каждом опубликовании NSNotification и отображает его в консоли gdb.

Я пытался создать точку трассировки в GDB, но не смог, потому что действия трассировки в GCD XCode, кажется, глючат - или, возможно, я просто слишком тупой, чтобы заставить их работать.

Я также пытался создать собственный скрипт Dtrace Instruments, но потерпел неудачу, так как мое 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.

21 голосов
/ 15 августа 2013

Привет, пока - я часто использую уведомления, и у меня были некоторые серьезные проблемы с их отладкой. Недавно я выпустил приложение под названием Spark Inspector (http://sparkinspector.com/)), которое немного облегчает процесс. Вы добавляете платформу в свое приложение, и она превращается в NSNotificationCenter, чтобы вы могли видеть таблицу всех отправленных и полученных уведомлений. в нашем приложении со стеками трассировок, куда они были отправлены, и списком всех методов, которые их наблюдали. Я знаю, что это примерно на три года позже, но это может помочь!

enter image description here

15 голосов
/ 04 мая 2015

Я знаю, что опубликованный вопрос старый, но я решил ответить парой строк кода.
Вы можете видеть все уведомления, опубликованные во время работы вашего приложения через этот блок:

  [[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 голосов
/ 25 октября 2016

В целях отладки я считаю, что точка останова на самом деле лучше, чем добавление кода в проект.Однако решение @ Till , похоже, не сработало для меня.Я нашел другое решение онлайн и немного его подправил.

Символическая точка останова

  • Символ : -[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).
...