Cocoa 为什么提出NSException不会导致我的应用程序失败?
问题Cocoa 为什么提出NSException不会导致我的应用程序失败?,cocoa,exception,multithreading,crash,raise,Cocoa,Exception,Multithreading,Crash,Raise,问题 #import "NSApplication+ExceptionHandling.h" @implementation NSApplication (ExceptionHandling) - (void)reportException:(NSException *)anException { (*NSGetUncaughtExceptionHandler())(anException); } @end void exceptionHandler(NSException *a
#import "NSApplication+ExceptionHandling.h"
@implementation NSApplication (ExceptionHandling)
- (void)reportException:(NSException *)anException
{
(*NSGetUncaughtExceptionHandler())(anException);
}
@end
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...
}
我正在编写一个Cocoa应用程序,我想提出一些异常,这些异常会导致应用程序崩溃
我的应用程序委托中有以下行:
[NSException raise:NSInternalInconsistencyException format:@"This should crash the application."];
abort();
问题是,它们不会关闭应用程序-消息只是记录到控制台,应用程序会以愉快的方式进行
据我所知,例外的全部意义在于,他们是在特殊情况下被解雇的。在这种情况下,我希望应用程序以明显的方式退出。但这并没有发生
我尝试过的
我试过:
-(void)applicationDidFinishLaunching:(NSNotification *)note
// ...
[self performSelectorOnMainThread:@selector(crash) withObject:nil waitUntilDone:YES];
}
-(void)crash {
[NSException raise:NSInternalInconsistencyException format:@"This should crash the application."];
abort();
}
这不起作用,而且
-(void)applicationDidFinishLaunching:(NSNotification *)note
// ...
[self performSelectorInBackground:@selector(crash) withObject:nil];
}
-(void)crash {
[NSException raise:NSInternalInconsistencyException format:@"This should crash the application."];
abort();
}
令人困惑的是,它的工作原理与预期相符
发生什么事了?我做错了什么?我发布了这个问题和答案,我希望有人在大约一年前告诉我: 在主线程上抛出的异常被NSApplication捕获。 我从头到尾浏览了NSException上的文档,没有提到我能回忆起来的内容。我知道这一点的唯一原因是因为神奇的可可开发: 解决方案。我猜。
我有一个没有UI的守护进程,它几乎完全在主线程上运行。我将不得不转移整个应用程序来运行后台线程,除非其他人可以建议一种方法来停止非应用程序捕获我抛出的异常。我很确定这是不可能的。也许你可以使用,或者在非应用程序上创建一个覆盖的类别,正如更新-2010年11月16日所建议的那样:在iAction方法中抛出异常时,这个答案会出现一些问题。请参见以下答案:
这扩展了David Gelhar的回答,以及他提供的链接。下面是我如何通过重写NSApplication的
-reportException:
方法实现的。首先,为NSApplication创建一个ExceptionHandling类别(仅供参考,您应该在“ExceptionHandling”之前添加一个2-3个字母的首字母缩写词,以减少名称冲突的风险):
n应用程序+例外处理.h
#import <Cocoa/Cocoa.h>
@interface NSApplication (ExceptionHandling)
- (void)reportException:(NSException *)anException;
@end
其次,在NSApplication的委托中,我做了以下工作: AppDelegate.m
#import "NSApplication+ExceptionHandling.h"
@implementation NSApplication (ExceptionHandling)
- (void)reportException:(NSException *)anException
{
(*NSGetUncaughtExceptionHandler())(anException);
}
@end
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...
}
您可以调用exit()
,而不是使用NSApp的terminate:
terminate:
更为洁净,但如果引发异常并使用exit()
硬崩溃,您可能希望跳过应用程序shouldTerminate:
代码:
无论何时在主线程上抛出异常,并且该异常未被捕获和销毁,您的自定义未捕获异常处理程序现在将被调用,而不是NSApplication的。这允许您使应用程序崩溃,以及其他一些事情
更新: 上面的代码似乎有一个小故障。在NSApplication完成调用其所有委托方法之前,您的自定义异常处理程序不会“启动”并工作。这意味着,如果您在应用程序内部执行一些设置代码,则将完成启动:或应用程序IDFinishLaunching:或从NIB:唤醒,默认的NSApplication异常处理程序在完全初始化之前似乎一直在运行 这意味着如果你这样做:
- (void)applicationWillFinishLaunching:(NSNotification *)aNotification
{
NSSetUncaughtExceptionHandler(&exceptionHandler);
MyClass *myClass = [[MyClass alloc] init]; // throws an exception during init...
}
您的异常处理程序将不会获得异常。非应用程序会,它只会记录它
要解决此问题,只需将任何初始化代码放入@try/@catch/@finally
块中,您就可以调用自定义的异常处理程序:
- (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子类化,而不是添加一个类别,它也会发生。这可能还附带了其他警告。我正试图正确理解这一点:为什么下面的非应用程序分类方法会导致无限循环?在该无限循环中,“引发了未捕获异常”被无限次注销:
- (void)reportException:(NSException *)anException
{
// handle the exception properly
(*NSGetUncaughtExceptionHandler())(anException);
}
为了测试(和理解目的),这是我唯一要做的事情,也就是说,只需创建上面的category方法。(根据中的说明)
为什么这会导致无限循环?这与默认的未捕获异常处理程序方法应该做的不一致,即只记录异常并退出程序。(见附件)
是否默认的未捕获异常处理程序实际上再次抛出异常,从而导致这个无限循环
注意:我知道只创建这个分类方法是愚蠢的。这样做的目的是为了更好地理解
更新:没关系,我想我现在明白了。这是我的照片。我们知道,默认情况下,NSApplication的reportException:方法记录异常。但是,根据文档,默认的未捕获异常处理程序记录异常并存在于程序中。但是,为了更精确,文档中应该这样表述:默认的未捕获异常处理程序调用NSApplication的reportException:方法(为了记录它,该方法的默认实现确实会这样做),然后存在程序。因此,现在应该清楚了,为什么在重写的reportException:中调用默认的未捕获异常处理程序会导致无限循环:前者调用后者结果是一个非常简单的解决方案: