J'ai un crash dans mon application iPhone qui lance une exception NSException. Les rapports d'erreur sont complètement ambigus quant à l'emplacement de l'erreur et à sa cause exacte. Existe-t-il un moyen intelligent de définir un gestionnaire d'exceptions de niveau supérieur quelque part pour voir ce qui en est la cause? Je ne peux pas reproduire le problème moi-même, mais certains de mes utilisateurs bêta le peuvent certainement.
Quelle est une façon intelligente de gérer un problème de cette nature?
Il semble que vous posiez deux questions ici: comment définir un gestionnaire d'exceptions de niveau supérieur; et comment traiter la question de la détermination de la cause profonde.
La capture de l'exception peut être effectuée de différentes manières, mais pour cela, la meilleure approche semble être de définir un gestionnaire d'exceptions à l'aide de NSSetUncaughtExceptionHandler.
Lorsqu'une exception est déclenchée dans votre application, elle est gérée par un gestionnaire d'exceptions par défaut. Ce gestionnaire ne fait rien de plus que d'enregistrer un message sur la console avant la fermeture de l'application. Vous pouvez contourner cela en définissant votre propre gestionnaire d'exceptions personnalisé à l'aide de la fonction indiquée ci-dessus. Le meilleur endroit pour le faire serait dans la méthode d'application délégué applicationDidFinishLaunching :.
- (void)applicationDidFinishLaunching:(UIApplication *)application
{
NSSetUncaughtExceptionHandler(&myExceptionHandler);
}
Une fois que vous avez défini un gestionnaire personnalisé, vous souhaiterez développer la sortie par défaut pour vous aider à déterminer la cause.
void myExceptionHandler(NSException *exception)
{
NSArray *stack = [exception callStackReturnAddresses];
NSLog(@"Stack trace: %@", stack);
}
Malheureusement, par rapport à OSX, l'iPhone semble assez limité en ce qui concerne la production d'une trace de pile Nice. Le code ci-dessus produira une sortie apparemment indésirable; cependant, vous pouvez exécuter cette sortie via l'outil atos et vous devriez pouvoir en générer une trace de pile utile.
Une autre option est de suivre les instructions sur cet article qui aideront à produire automatiquement une trace de pile Nice.
Comme cela va aux bêta-testeurs, vous devrez peut-être bricoler pour le faire fonctionner pour vous.
Vous dites que vous n'avez pas pu reproduire le problème vous-même, seulement vos utilisateurs. Dans ce cas, vous pouvez trouver cette note technique de Apple utile:
https://developer.Apple.com/library/content/technotes/tn2151/_index.html
[~ # ~] update [~ # ~] : Bien que cet article contienne encore des informations utiles, certains des liens qu'il contient sont morts de manière irréversible. Il est conseillé d'utiliser les informations de this post alternatif.
Si vous prévoyez de le faire par vous-même, vous pouvez utiliser l'une de ces approches
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
}
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
}
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
}
À mon avis, n'essayez pas d'envoyer les détails de l'exception au serveur au moment du plantage, envoyez-le lorsqu'il redémarre l'application.
Si vous allez utiliser NSUserDefaults pour enregistrer les détails de l'exception, vous devez le synchroniser au moment du plantage sinon il ne persistera pas.
L'extrait de code suivant fait le travail.
- (void)applicationWillTerminate:(UIApplication *)application
{
[[NSUserDefaults standardUserDefaults]synchronize];
}
Dans XCode, vous devez toujours définir un point d'arrêt global pour objc_exception_throw
. Ensuite, vous obtenez (généralement) une trace de pile beaucoup plus significative quant à ce qui essaie réellement de lever une exception.
Vous pouvez toujours obtenir des exceptions qui proviennent du code du minuteur ou d'autres endroits sans votre propre code n'importe où dans la trace, mais si vous regardez la chaîne de méthodes, vous pouvez généralement comprendre en général de quoi il s'agit (comme avoir une notification envoyée à l'endroit où la cible est parti).
Avez-vous essayé NSSetUncaughtExceptionHandler
?
Une autre option pour suivre les rapports de plantage est Plausible CrashReporter, code source ouvert pour vous envoyer automatiquement des rapports de plantage depuis le terrain.
Il y a aussi le CrashReporterDemo, une autre option open source qui est une combinaison de Plausible CrashReporter et du code serveur pour mieux suivre les rapports de plantage .
Et enfin, il y a MacDevCrashReporter, un service qui semble avoir des similitudes avec iOSExceptional.com, suggéré dans une autre réponse. Je n'ai aucune idée de leurs conditions de service, car je ne me suis pas inscrit à la version bêta. Vaut vraiment la peine de vérifier avant d'entrer trop profondément.
Découvrez Crittercism . Cela va au-delà de ce que vous demandez ici en ce qu'il vous permet d'obtenir ces informations pour tous les utilisateurs utilisant votre application, vous devriez donc être en mesure de voir votre propre plantage.
Vous pouvez également télécharger le DYSM pour votre version particulière et il symbolisera automatiquement le crash pour vous sur leur site Web. Cela devrait vous fournir la trace de pile la plus claire sans être connecté au débogueur.
Vous pouvez également vous assurer que vous êtes prêt à rompre avec les exceptions Objetive-C. Dans Xcode 4, dans l'onglet points d'arrêt, vous pouvez ajouter une exception de point d'arrêt qui rompt sur les deux exceptions C++ et Obj-C. Sans cela, la plupart des traces de pile pour les exceptions levées sont assez inutiles.
Bonne chance!