35

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

虽然我很天真,但我尝试的第一件事就是这样注册:

在我的应用程序的某个地方:

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

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

实际上,我确实以这种方式收到了许多通知。但在某些时候,应用程序确实崩溃了。堆栈跟踪显示它在 implementationClass 中因 EXC_BAD_ACCESS 而崩溃,根据我的经验,这确实表明某些东西在其释放后被调用。然而我的观察对象仍然活着,它的释放器还没有被调用。

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

我错过了什么?是否有可靠地观察 NSNotificationCenter 的方法(例如工具)?

感谢您的任何提示。

4

5 回答 5

60

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

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

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

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

我确实尝试在 gdb 中创建一个跟踪点,但失败了,因为 Xcode gdb 中的跟踪点操作似乎有问题——或者我可能只是太笨了,无法让它们工作。

我还尝试创建一个自定义 Instruments Dtrace 脚本,但失败了,因为我的 Dtrace 空手道不够强。

如果您设法使后面的任何选项起作用,请继续将它们作为替代答案发布-我会投票并将它们标记为最喜欢的选项。

更新

在这个问题之后很久,我找到了在 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的界面,其实我有点惭愧。

于 2010-09-17T19:31:02.170 回答
21

Hey Till—I use notifications a lot, and I had some serious problems debugging them as well. I recently released an app called the Spark Inspector (http://sparkinspector.com/) that makes the process a bit easier. You add a framework to your app, and it swizzles NSNotificationCenter so you can see a table of all of the notifications sent and received in y our app, with the stack traces where they were sent and the list of all the methods that observed them. I know it's about three years late, but it may help!

enter image description here

于 2013-08-15T05:31:15.673 回答
17

我知道发布的问题很旧,但我想我会用几行代码来回应。
您可以通过此块查看应用运行时发布的所有通知:

  [[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)
}
于 2015-05-04T17:46:35.927 回答
11

出于调试目的,我发现断点实际上比在项目中添加代码要好。但是,@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)184467440737095516159223372036854775807
于 2016-10-25T05:16:35.207 回答
1

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

CFNotificationCenterAddObserver(
    CFNotificationCenterGetLocalCenter(),
    nil,
    { (notificationCenter, _, notificationName, _, dictionary) in
        print(notificationName)
        print(dictionary)
}, nil, nil, .deliverImmediately)
于 2019-10-19T14:27:34.317 回答