更新 - Nov 16, 2010: 当在IBAction方法中抛出异常时,此答案存在一些问题。请查看以下答案:
如何阻止HIToolbox捕获我的异常?
以下是我通过覆盖NSApplication的-reportException:
方法来完成的,这扩展了David Gelhar的答案和他提供的链接。首先,为NSApplication创建一个ExceptionHandling类别(FYI,您应该在“ExceptionHandling”之前添加一个2-3个字母的缩写以减少名称冲突的风险):
NSApplication+ExceptionHandling.h
#import <Cocoa/Cocoa.h>
@interface NSApplication (ExceptionHandling)
- (void)reportException:(NSException *)anException;
@end
NSApplication+ExceptionHandling.m
#import "NSApplication+ExceptionHandling.h"
@implementation NSApplication (ExceptionHandling)
- (void)reportException:(NSException *)anException
{
(*NSGetUncaughtExceptionHandler())(anException);
}
@end
第二步,我在NSApplication的代理中执行以下操作:
AppDelegate.m
void exceptionHandler(NSException *anException)
{
NSLog(@"%@", [anException reason]);
NSLog(@"%@", [anException userInfo]);
[NSApp terminate:nil];
}
- (void)applicationWillFinishLaunching:(NSNotification *)aNotification
{
NSSetUncaughtExceptionHandler(&exceptionHandler);
}
与其使用NSApp的terminate:
方法,您可以改为调用exit()
。虽然terminate:
更符合Cocoa规范,但如果出现异常并且您想直接崩溃,可以跳过applicationShouldTerminate:
代码,改用exit()
:
// ...
exit(EX_SOFTWARE);
每当在
主线程上抛出异常并且未被捕获和销毁时,您的自定义未捕获异常处理程序将被调用,而不是NSApplication的处理程序。这使您可以使应用程序崩溃,以及其他操作。
更新:
上述代码中似乎存在一个小错误。只有在NSApplication完成调用其所有委托方法后,您的自定义异常处理程序才会开始运行。这意味着如果您在applicationWillFinishLaunching:或applicationDidFinishLaunching:或awakeFromNib:中执行一些设置代码,则默认的NSApplication异常处理程序似乎仍然有效,直到完全初始化为止。
这意味着如果您执行以下操作:
- (void)applicationWillFinishLaunching:(NSNotification *)aNotification
{
NSSetUncaughtExceptionHandler(&exceptionHandler);
MyClass *myClass = [[MyClass alloc] init];
}
你的exceptionHandler不会获取异常。NSApplication会获取异常并将其记录。
要修复此问题,只需将任何初始化代码放入@try/@catch/@finally
块中,并可以调用自定义的exceptionHandler:
- (void)applicationWillFinishLaunching:(NSNotification *)aNotification
{
NSSetUncaughtExceptionHandler(&exceptionHandler);
@try
{
MyClass *myClass = [[MyClass alloc] init];
}
@catch (NSException * e)
{
exceptionHandler(e);
}
@finally
{
}
}
现在您的
exceptionHandler()
会获取异常并可以相应地处理它。在NSApplication完成调用所有委托方法之后,
NSApplication+ExceptionHandling.h分类启动,通过其自定义的
-reportException:
方法调用exceptionHandler()。此时,当您希望将异常上升到未捕获的异常处理程序时,无需担心@try/@catch/@finally。
我对导致这种情况的原因有点困惑。可能是API中的某些幕后操作。即使我子类化NSApplication,而不是添加类别,它也会发生。还可能存在其他注意事项。