iOS: исключения фоновых потоков не вызывают сбоев

Я не нашел документации, согласующейся с моим опытом.

Мне нужен хороший способ обработки Uncaught-Exceptions в фоновом потоке. Этот «способ» должен допускать сбой приложения, но выполнять некоторые очень простые операции перед сбоем (например, сохранять значение в UserDefaults, чтобы его можно было проверить при следующем запуске; плюс ведение журнала).

В основном потоке я только что установил uncaughtExceptionHanlder, и все работает нормально. Однако в фоновом потоке, выполняемом как NSOperation в NSOperationQueue, возникает любое исключение, но не выход из приложения: сбой. Приложение продолжает работать в поврежденном состоянии.

Однако в Руководстве по программированию потоков говорится:

Настройка обработчика исключений Если ваше приложение перехватывает и обрабатывает исключения, код вашего потока должен быть готов к перехвату любых исключений, которые могут возникнуть. Хотя лучше всего обрабатывать исключения в том месте, где они могут возникнуть, неспособность перехватить выброшенное исключение в потоке приведет к завершению работы приложения. Установка финальной команды try/catch в подпрограмме входа в поток позволяет перехватывать любые неизвестные исключения и предоставлять соответствующий ответ.

Один из работающих методов (см. ниже) — встраивание метода вызова потока с помощью try/catch, а в случае исключения — регистрация и последующий вызов abort(). Но это не лучший способ сделать это. Я хотел бы отправить исключение в основной поток и обработать его обработчиком необработанных исключений. Кто-нибудь делал это?

- (void)threadMethod
{
    @try
    {
        NSArray* aTest = [NSArray array];
        [aTest objectAtIndex:10];
    }
    @catch (NSException* e)
    {
        // Save to state to User Defaults.
        // Log any needed info.

        abort();
    }
    @finally
    {

    }
}

К вашему сведению: я работаю на iOS6 с XCode 4.5 SDK.


person paiego    schedule 12.01.2013    source источник


Ответы (2)


  1. Потоки, запущенные NSOperationQueue, управляются libdispatch, который перехватывает исключения и вызывает terminate, выходя из приложения. Если вы видите другое поведение, вы уже делаете что-то не так.
  2. Сохранение данных в NSUserDefaults после исключения является сомнительным предложением; поскольку Cocoa обрабатывает исключения только как ошибку программиста, он не делает никаких попыток оставить себя в пригодном для использования состоянии после того, как одно из них было выброшено. Короче говоря, вы должны относиться к этому так же, как к «настоящему» сбою, как если бы доступны только API, безопасные для асинхронных сигналов. Что-либо, связанное с Objective-C, автоматически исключается в этом отношении.
  3. Ваш вопрос предполагает, что вы пытаетесь сделать отчет о сбоях. Я бы порекомендовал решение для создания отчетов о сбоях, например PLCrashReporter. Существует также ряд служб анализа и распространения, в которые встроены отчеты о сбоях, в том числе HockeyApp, Crashlytics, TestFlight и QuincyKit. Есть и другие, и какой из них подходит вам, зависит от ваших потребностей. Все они будут решать все сложные вопросы, связанные с безопасной обработкой сбоев и исключений и сохранением данных на потом, не беспокоясь ни о чем из этого.
person Gwynne Raskind    schedule 12.01.2013
comment
Это не проблема отчетов о сбоях. В случае сбоя я хочу сохранить информацию о состоянии, используемую для следующего запуска. - person paiego; 14.01.2013
comment
Навскидку, я бы сказал, что лучше всего записывать данные в файл в настолько простом формате, насколько вы можете управлять, используя простые API-интерфейсы POSIX. Опять же, я должен подчеркнуть, что все, что использует Objective-C, небезопасно в тот момент, когда исключение может привести к сбою вашего приложения; все в неизвестном состоянии. Очень быстрый пример, который сразу приходит мне в голову: int fd = open(UTF8StringOfDocumentsDirectoryPlusFileNameSavedAtAppStartup, O_WRONLY | O_CREAT | O_TRUNC); if (fd != -1) { write(fd, "crashed", sizeof("crashed") - 1); close(fd); } - person Gwynne Raskind; 15.01.2013

Для того, что вы делаете, я считаю, что вам понадобится код в вашем основном потоке, который проверяет, что происходит во втором потоке. Поскольку у вас нет возможности фактически связаться с основным потоком и запустить событие из фонового потока, вы, скорее всего, застрянете в написании чего-то в своем основном потоке, который время от времени проверяет. Тем не менее, вы, вероятно, могли бы работать с NSNotificationCenter и заставить основной поток запускать событие, когда исключение выдается из фонового потока. Удачи.

person Tony    schedule 13.01.2013