为什么这个 Objective C 调用似乎会挂起?

6

我的一个朋友在使用NSDictionary时发现了一些奇怪的行为,我很好奇它是如何发生的。请看下面的代码:

NSDictionary *dict = [[NSDictionary alloc] init];

// Oops, we can't mutate an NSDictionary
[dict setObject:[[NSNull alloc] init] forKey:@"test"];
NSLog(@"Set");

该代码在编译时会产生警告,提示“'NSDictionary'可能无法响应'setObject:forKey:'”。这很好理解,如果您仍然运行它,您将在控制台中获得以下输出:
- [__NSCFDictionary setObject:forKey:]:发送到不可变对象的改变方法
同样,这正是您期望发生的事情。但是,在此时应用程序不会崩溃或由于未捕获的异常而终止。setObject:forKey:方法根本没有返回,应用程序似乎停顿了;以下的NSLog从未执行。如果您尝试使用GDB跨越或进入该方法,调试似乎就会结束,但没有任何明确的错误消息。应用程序继续运行,但调试器并没有提供有关执行被“卡住”的代码位置的线索。
这里发生了什么?在这种情况下,应用程序实际上在做什么,为什么不会崩溃出现NSInternalInconsistencyException或类似的东西? 编辑:对于那些问过的人,我在运行OS X Lion(10.7.2)上的XCode 4.1,使用“Apple LLVM编译器2.1”构建。我使用XCode 4中新的Cocoa项目默认设置。无论是调试程序还是仅“运行”它,我都遇到了相同的非崩溃行为。从Debug构建更改为Release构建没有任何区别。我甚至可以手动在Finder中找到.app文件并双击它以在XCode之外执行它,它仍然不会崩溃。

1
尝试在调试器下不运行它;我敢打赌它会崩溃。我发现调试器环境可能会对运行时环境产生一些意外的影响,特别是在异常和触发异常时会发生什么。 - Ben Zotto
2
当异常被抛出时,控制流将会转移到捕获该异常的代码块,而不是跳到异常之后的下一行。在你的例子中,无论是调试器还是运行时的内部机制,都已经“捕获”了异常。在任何情况下,你都不应该期望在 -setObject:forKey: 抛出异常后仍然会发生 NSLog。 - Vincent Gable
@VincentGable:同意,如果程序崩溃了,我一点也不会惊讶NSLog没有被调用。令我惊讶的是它既不崩溃也不执行下一行。它似乎应该做其中之一。对于那些问过我的人,我已经更新了问题,并提供了一些关于我运行环境的额外细节。 - Mitch Lindgren
这基本上是 https://dev59.com/63A75IYBdhLWcg3wW3u8 的副本。 - matt
1个回答

1

异常不会使AppKit程序崩溃。NSApplication安装了一个默认的异常处理程序,可以捕获您的代码未处理的异常。然后您可以像平常一样返回到运行循环中。

很多应用程序都表现出这种行为。这是造成突然出现空白视图/窗口的常见原因。如果在视图成功绘制之前发生异常,视图将变为空白,但应用程序不会崩溃。只有当您故意更改默认的异常处理程序以崩溃时,异常才会导致崩溃。


谢谢,这基本上回答了我的问题。但是,我仍然困惑的一件事是,在异常被捕获后为什么我看不到应用程序返回到主事件循环。或者那是在不同的线程中发生的吗?我对AppKit的内部工作并不是很熟悉。 - Mitch Lindgren

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接