更新 - 2010 年 11 月 16 日:当 IBAction 方法中引发异常时,此答案存在一些问题。请参阅此答案:
如何阻止 HIToolbox 捕获我的异常?
这扩展了David Gelhar 的回答以及他提供的链接。下面是我如何通过覆盖 NSApplication 的-reportException:
方法来做到这一点。首先,为 NSApplication 创建一个 ExceptionHandling 类别(仅供参考,您应该在“ExceptionHandling”之前添加一个 2-3 个字母的首字母缩写词,以减少名称冲突的风险):
NSApplication+异常处理.h
#import <Cocoa/Cocoa.h>
@interface NSApplication (ExceptionHandling)
- (void)reportException:(NSException *)anException;
@end
NSApplication+异常处理.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]; // you can call exit() instead if desired
}
- (void)applicationWillFinishLaunching:(NSNotification *)aNotification
{
NSSetUncaughtExceptionHandler(&exceptionHandler);
// additional code...
// NOTE: See the "UPDATE" at the end of this post regarding a possible glitch here...
}
terminate:
您可以调用而不是使用 NSApp exit()
。terminate:
更多的是 Cocoa-kosher,尽管您可能希望applicationShouldTerminate:
在引发异常的情况下跳过您的代码并且简单地使用以下命令进行硬崩溃exit()
:
#import "sysexits.h"
// ...
exit(EX_SOFTWARE);
每当在主线程上抛出异常并且它没有被捕获和销毁时,现在将调用您的自定义未捕获异常处理程序而不是 NSApplication 的。这使您可以使您的应用程序崩溃,等等。
更新:
上面的代码似乎有一个小故障。在 NSApplication 完成调用其所有委托方法之前,您的自定义异常处理程序不会“启动”并工作。这意味着如果您在applicationWillFinishLaunching:或applicationDidFinishLaunching:或awakeFromNib:中执行一些设置代码,则默认的 NSApplication 异常处理程序似乎正在运行,直到它完全初始化。
这意味着如果你这样做:
- (void)applicationWillFinishLaunching:(NSNotification *)aNotification
{
NSSetUncaughtExceptionHandler(&exceptionHandler);
MyClass *myClass = [[MyClass alloc] init]; // throws an exception during init...
}
您的exceptionHandler不会得到异常。NSApplication 会,它只会记录它。
要解决这个问题,只需将任何初始化代码放在一个@try/@catch/@finally
块中,您就可以调用您的自定义exceptionHandler:
- (void)applicationWillFinishLaunching:(NSNotification *)aNotification
{
NSSetUncaughtExceptionHandler(&exceptionHandler);
@try
{
MyClass *myClass = [[MyClass alloc] init]; // throws an exception during init...
}
@catch (NSException * e)
{
exceptionHandler(e);
}
@finally
{
// cleanup code...
}
}
现在你exceptionHandler()
得到了异常并可以相应地处理它。在 NSApplication 调用完所有委托方法后,NSApplication+ExceptionHandling.h类别开始执行,通过其自定义-reportException:
方法调用 exceptionHandler()。此时,当您希望将异常引发到您的未捕获异常处理程序时,您不必担心@try/@catch/@finally。
我对造成这种情况的原因感到有些困惑。可能是 API 的幕后工作。即使我将 NSApplication 子类化,而不是添加一个类别,它也会发生。可能还有其他警告。