Проблема
Я пишу приложение Какао, и я хочу повысить исключения, которые разрушат приложение шумно.
У меня есть следующие строки в моем делегате приложения:
[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();
}
который, скорее смутно, работает как ожидалось.
Что продолжается? Что я делаю неправильно?
UPDATE - Nov 16, 2010: Есть некоторые проблемы с этим ответом, когда исключения бросаются внутри методов IBAction. Посмотрите этот ответ вместо него:
Как я могу запретить HIToolbox перехватывать мои исключения?
Это развитие ответа David Gelhar и ссылки, которую он предоставил. Ниже описано, как я это сделал, переопределив метод -reportException:
NSApplication. Сначала создайте категорию ExceptionHandling для NSApplication (к сведению, перед "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]; // 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, хотя вы можете захотеть пропустить код 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, вызывая exceptionHandler() через свой собственный метод -reportException:
. Теперь вам не нужно беспокоиться о @try/@catch/@finally, когда вы хотите, чтобы исключения передавались в обработчик не пойманных исключений.
Я немного озадачен тем, что вызывает это. Возможно, что-то за кадром в API. Это происходит даже когда я создаю подкласс NSApplication, а не добавляю категорию. Могут быть и другие предостережения, связанные с этим.
Возможно, вы можете использовать NSSetUncaughtExceptionHandler или создать категорию в NSApplication, которая переопределяет -reportException: , как предлагается на http : //www.cocoadev.com/index.pl? StackTraces
Я разместил этот вопрос и ответ, поскольку хотел бы, чтобы кто-нибудь сказал мне это, о, около года назад:
Исключения, создаваемые в основном потоке, перехватываются NSApplication.
Я бегло прочитал документацию по NSException от начала до конца, без упоминания об этом, насколько я могу припомнить. Единственная причина, по которой я знаю это, - фантастический Cocoa Dev:
http://www.cocoadev.com/index.pl?ExceptionHandling
Решение. Наверное.
У меня есть демон без пользовательского интерфейса, который почти полностью работает в основном потоке. Мне придется передать все приложение для запуска фоновых потоков, если кто-то другой не предложит способ остановить NSApplication, перехватывающего только выбрасываемые мной исключения. Я почти уверен, что это невозможно.