Почему повышение является NSException, не снижающим мое приложение?

Проблема

Я пишу приложение Какао, и я хочу повысить исключения, которые разрушат приложение шумно.

У меня есть следующие строки в моем делегате приложения:

[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();
}

который, скорее смутно, работает как ожидалось.

Что продолжается? Что я делаю неправильно?

10
задан John Gallagher 26 July 2010 в 15:22
поделиться

3 ответа

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 будет вызван ваш собственный обработчик не пойманных исключений. Это позволит вам, помимо прочего, аварийно завершить работу приложения.


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, когда вы хотите, чтобы исключения передавались в обработчик не пойманных исключений.

Я немного озадачен тем, что вызывает это. Возможно, что-то за кадром в API. Это происходит даже когда я создаю подкласс NSApplication, а не добавляю категорию. Могут быть и другие предостережения, связанные с этим.

10
ответ дан 3 December 2019 в 17:19
поделиться

Возможно, вы можете использовать NSSetUncaughtExceptionHandler или создать категорию в NSApplication, которая переопределяет -reportException: , как предлагается на http : //www.cocoadev.com/index.pl? StackTraces

3
ответ дан 3 December 2019 в 17:19
поделиться

Я разместил этот вопрос и ответ, поскольку хотел бы, чтобы кто-нибудь сказал мне это, о, около года назад:

Исключения, создаваемые в основном потоке, перехватываются NSApplication.

Я бегло прочитал документацию по NSException от начала до конца, без упоминания об этом, насколько я могу припомнить. Единственная причина, по которой я знаю это, - фантастический Cocoa Dev:

http://www.cocoadev.com/index.pl?ExceptionHandling

Решение. Наверное.

У меня есть демон без пользовательского интерфейса, который почти полностью работает в основном потоке. Мне придется передать все приложение для запуска фоновых потоков, если кто-то другой не предложит способ остановить NSApplication, перехватывающего только выбрасываемые мной исключения. Я почти уверен, что это невозможно.

2
ответ дан 3 December 2019 в 17:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: