Как вы реализуете глобальную обработку исключений iPhone?


У меня есть один сбой в моем приложении iPhone, который бросает исключение NSException. Отчеты о сбоях полностью неоднозначны в том, где ошибка и что именно вызывает ее. Есть ли разумный способ для меня, чтобы установить обработчик исключений верхнего уровня где-нибудь, чтобы увидеть, что вызывает его? Я не могу воспроизвести проблему сам, но некоторые из моих бета-пользователей, безусловно, может.

какой умный способ справиться с проблемой такого характера?

6 58

6 ответов:

похоже, что вы задаете здесь два вопроса: Как установить обработчик исключений верхнего уровня; и как справиться с проблемой определения того, что является основной причиной.

Перехват исключения можно сделать несколькими различными способами, но для этого лучшим подходом будет установить обработчик исключений с помощью NSSetUncaughtExceptionHandler.

когда в вашем приложении возникает исключение, оно обрабатывается обработчиком исключений по умолчанию. Этот обработчик больше ничего не делает чем войти сообщение на консоль до закрытия приложения. Вы можете преодолеть это, установив собственный обработчик исключений с помощью функции, указанной выше. Лучшее место для этого было бы в делегате приложения applicationDidFinishLaunching: метод.

- (void)applicationDidFinishLaunching:(UIApplication *)application
{
    NSSetUncaughtExceptionHandler(&myExceptionHandler);
}

после установки пользовательского обработчика вы захотите расширить вывод по умолчанию,чтобы помочь вам определить причину.

void myExceptionHandler(NSException *exception)
{
    NSArray *stack = [exception callStackReturnAddresses];
    NSLog(@"Stack trace: %@", stack);
}

к сожалению, по сравнению с OSX iPhone выглядит довольно ограниченным уважение к созданию хорошей трассировки стека. Приведенный выше код будет производить некоторые, казалось бы, ненужные выходные данные; однако вы можете запустить этот вывод через инструмент atos, и вы должны иметь возможность генерировать полезную трассировку стека из него.

другой вариант-следовать инструкциям на в этой статье что поможет автоматически создать хорошую трассировку стека.

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

вы говорите, что не смогли воспроизвести проблему самостоятельно, только ваши пользователи. В этом случае вы можете найти это техническое Примечание от Apple полезным:

https://developer.apple.com/library/content/technotes/tn2151/_index.html

обновление: хотя этот пост по-прежнему содержит полезную информацию, некоторые ссылки, которые он содержит, безвозвратно мертвы. Рекомендуется использовать информацию от этой альтернатива должность.

если вы планируете сделать это самостоятельно, вы можете использовать один из этих подходов

Approach1:

void onUncaughtException(NSException* exception)
{
//save exception details
}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  NSSetUncaughtExceptionHandler(&onUncaughtException);
  //Add coding to find if any exception has occurred from saved details if so send it to server or ask user to comment on the issue.
//Rest of the coding
}

Approach2:

void onUncaughtException(NSException* exception)
{

//Save exception details

}

int main(int argc, char *argv[])
{
    @autoreleasepool {

        NSSetUncaughtExceptionHandler(&onUncaughtException);

        return UIApplicationMain(argc, argv, nil, NSStringFromClass([SGGI_AppDelegate class]));
    }
}



-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
 {
      //Add coding to find if any exception has occurred from saved details if so send it to server or ask user to comment on the issue.
    //Rest of the coding
 }

Approcach3:

int main(int argc, char *argv[])
{
    @autoreleasepool {

        @try {

            return UIApplicationMain(argc, argv, nil, NSStringFromClass([SGGI_AppDelegate class]));
        }
        @catch (NSException *exception) {      
        //Save the exception
        }
        @finally {
        }

    }
}

-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
 {
      //Add coding to find if any exception has occurred from saved details if so send it to server or ask user to comment on the issue.
    //Rest of the coding
 }

Примечание:

  • С моей точки зрения, не пытайтесь отправить сведения об исключении с сервером в момент сбоя отправить его, когда он снова запускает приложение.

  • если вы собираетесь использовать NSUserDefaults для сохранения сведений об исключении, то вы должны синхронизировать его во время сбоя, иначе он не будет сохраняться.

следующий фрагмент кода выполняет работу.

   - (void)applicationWillTerminate:(UIApplication *)application
   {
       [[NSUserDefaults standardUserDefaults]synchronize];
   }
  • если вы предпочитаете сохранить его на SQLite db, то он сохраняется сам по себе нет необходимости вызывать что-либо, чтобы сохраниться во время сбоя

в XCode, вы всегда должны установить глобальную точку останова для objc_exception_throw. Затем вы (обычно) получаете гораздо более значимую трассировку стека относительно того, что на самом деле пытается вызвать исключение.

вы все еще можете получить исключения, которые происходят из кода таймера или других мест без вашего собственного кода в любом месте трассировки, но если вы посмотрите на цепочку методов, вы обычно можете понять, что такое исключение (например, отправив уведомление, где цель ушла).

ты пробовал NSSetUncaughtExceptionHandler?

еще один вариант для отслеживания отчетов о сбоях является Правдоподобный CrashReporter открытый исходный код автоматически отправлять вам отчеты о сбоях с поля.

есть еще CrashReporterDemo, еще один вариант с открытым исходным кодом, который представляет собой комбинацию правдоподобного CrashReporter и некоторого кода сервера для лучшего отслеживания отчетов о сбоях.

и наконец, MacDevCrashReporter, a сервис, который, как представляется, имеет сходство с iOSExceptional.com-предложил другой ответ. Я понятия не имею, каковы их условия обслуживания, так как я не подписался на бета-версию. Определенно стоит проверить, прежде чем войти слишком глубоко.

проверить Crittercism. Это выходит за рамки того, что вы просите здесь в том, что он позволяет получить эту информацию для всех пользователей, использующих ваше приложение, так что вы должны быть в состоянии увидеть свой собственный сбой.

вы также можете загрузить DYSM для вашей конкретной сборки, и он автоматически будет символизировать сбой для вас на своем веб-сайте. Это должно предоставить вам самую четкую трассировку стека без подключения к отладчику.

вы также можете захотеть убедитесь, что вы настроены на разрыв в исключениях Objective-C. В Xcode 4 на вкладке точки останова можно добавить исключение точки останова, которое прерывается на исключениях C++ и Obj-C. Без этого большинство трассировок стека для исключений, брошенных, довольно бесполезны.

удачи!