ОБНОВЛЕНИЕ - 16 ноября 2010 г .: Есть некоторые проблемы с этим ответом, когда в методах IBAction возникают исключения. Смотрите этот ответ вместо:
Как я могу помешать HIToolbox перехватывать мои исключения?
Это расширяет ответ Дэвида Гелхара и ссылку, которую он предоставил. Ниже показано, как я это сделал, переопределив метод -reportException:
NSApplication. Во-первых, создайте категорию ExceptionHandling для NSApplication (к вашему сведению, вы должны добавить 2-3-буквенную аббревиатуру перед «ExceptionHandling», чтобы уменьшить риск конфликта имен):
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:
является более какао-кошерным, хотя вы можете пропустить свой код applicationShouldTerminate:
в случае, если возникло исключение, и просто аварийно завершить работу с exit()
:
#import "sysexits.h"
// ...
exit(EX_SOFTWARE);
Всякий раз, когда выдается исключение, в главном потоке , и оно не перехватывается и не уничтожается, ваш собственный обработчик необработанных исключений теперь будет вызываться вместо NSApplication. Это позволяет, помимо прочего, аварийно завершить работу приложения.
UPDATE:
В приведенном выше коде, похоже, есть небольшая ошибка. Ваш пользовательский обработчик исключений не будет включаться и работать до тех пор, пока 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, когда вы хотите, чтобы исключения обрабатывались вашим обработчиком исключений Uncaught.
Я немного сбит с толку тем, что вызывает это. Возможно, что-то закулисное в API. Это происходит даже тогда, когда я делаю подкласс NSApplication, а не добавляю категорию. К этому также могут быть приложены другие оговорки.