NSNotificationCenter捕获并跟踪所有NSNotifications

时间:2010-09-16 09:11:19

标签: iphone trace nsnotifications

为了更好地理解“引擎盖下”发生的事情,我很乐意对我的应用程序中发生的任何通知进行完整的跟踪。

天真如初,我尝试的第一件事就是这样注册:

我的应用中的某处:

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

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

我确实收到了一些通知。但在某些时候,应用程序确实崩溃了。堆栈跟踪显示它在realizeClass中与EXC_BAD_ACCESS崩溃,根据我的经验,它确实表明在释放后调用了某些东西。然而,我的观察对象仍然存在,它的解除分配器尚未被调用。

我尝试的下一件事是设置一个断点到-[NSNotificationCenter postNotification:],然后每当我的断点被捕获时在gdb-console内运行po {NSNotification *}($ebp+16)。这确实揭示了一些通知,但不是我期望/希望的所有通知。例如,我的应用程序确实正确处理了方向更改,但在重新定位设备时(模拟器中)没有看到任何通知被捕获。

我错过了什么? 有没有办法(例如工具)可靠地观察NSNotificationCenter?

感谢任何提示。

5 个答案:

答案 0 :(得分:57)

我开始工作的唯一解决方案是使用断点。

我在__CFXNotificationPost_old(CoreFoundation)添加了一个断点,并将其与调试器命令po {NSNotification *}($ebp+12)捆绑在一起。所有这些在Xcode GUI中都可以实现:

  • 点击Xcode应用程序菜单(屏幕顶部)上的“运行”
  • 选择“调试器”
  • 在调试器窗口中
  • 单击“Show-Breakpoints”
  • 点击“输入符号名称”-line并输入“__CFXNotificationPost_old”
  • 点击最右侧的“+”
  • 在该下拉列表中选择“调试器命令”
  • 输入“po {NSNotification *}($ ebp + 12)
  • (您可能还想通过选中底部的“日志”复选框激活日志记录)
  • 在Xcode
  • 中的模拟器调试会话中运行您的应用

每当发布NSNotification时,应用程序都会停止执行,并将其显示在gdb-console中。

我确实尝试在gdb中创建一个跟踪点但是失败了,因为Xcode gdb中的跟踪点操作似乎有些错误 - 或者我可能只是太过stoopid而无法让它们工作。

我也试图创建一个自定义的Instruments Dtrace脚本,但由于我的Dtrace Karate不够强大而失败了。

如果你设法让后面的任何一个选项起作用,请继续发布它们作为另一种选择 - 我会赞成并将它们标记为受欢迎的选项。

<强>更新

在这个问题之后的年龄,我找到了正确的方法来捕获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的界面。

答案 1 :(得分:21)

嘿直到 - 我经常使用通知,我也有一些严重的问题调试它们。我最近发布了一个名为Spark Inspector(http://sparkinspector.com/)的应用程序,它使这个过程变得更容易一些。您向应用程序添加了一个框架,它会调动NSNotificationCenter,以便您可以在我们的应用程序中查看发送和接收的所有通知的表格,其中包含发送它们的堆栈跟踪以及观察它们的所有方法的列表。我知道它已经晚了三年,但它可能有所帮助!

enter image description here

答案 2 :(得分:15)

我知道发布的问题已经过时了,但我想我会用几行代码回复。
您可以通过此块查看应用程序运行时发布的所有通知:

  [[NSNotificationCenter defaultCenter] addObserverForName:nil
                                                    object:nil
                                                     queue:nil
                                                 usingBlock:^(NSNotification *notification) {
    NSLog(@"%@", notification.name);
  }];

将其添加到适当视图控制器的viewWillAppear方法中。 (当然,在准备应用程序以进行任何类型的分发时,您应该从代码中删除它。)

另外,请务必添加:

[[NSNotificationCenter defaultCenter] removeObserver:self];

到您选择的视图控制器对应的viewWillDisappear方法。

更新: 同样的答案,但在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)
}

答案 3 :(得分:5)

出于调试目的,我发现断点实际上比向项目添加代码更好。但是,https://developer.mozilla.org/en/docs/Web/CSS/pointer-events似乎对我不起作用。我找到了@Till's solution,并稍微调整了一下。

  

符号断点

     
      
  • 符号-[NSNotificationCenter postNotificationName:object:userInfo:]
  •   
  • 条件((NSRange)[$arg3 rangeOfString:@"^(_|NS|UI)" options:1024]).length == 0
  •   
  • 操作:调试器命令po $arg3
  •   
  • 评估操作后自动继续
  •   

注意:

  • 此条件阻止显示以_NSUI开头的所有通知。
  • 1024指的是another solution online,它似乎不适用于该断点。
  • 我使用.length == 0代替.location == NSNotFound,因为NSNotFound似乎评估的值与此断点中返回的值不同(-1(NSUInteger)18446744073709551615) (9223372036854775807)。

答案 4 :(得分:0)

@直到 Swift 5.0和Xcode 11 中的解决方案:

CFNotificationCenterAddObserver(
    CFNotificationCenterGetLocalCenter(),
    nil,
    { (notificationCenter, _, notificationName, _, dictionary) in
        print(notificationName)
        print(dictionary)
}, nil, nil, .deliverImmediately)