¿Cómo se implementa el manejo global de excepciones de iPhone?
objective-c cocoa-touch (6)
¿Has probado NSSetUncaughtExceptionHandler
?
Tengo un bloqueo en mi aplicación de iPhone que arroja una NSException. Los informes de fallos son completamente ambiguos en cuanto a dónde está el error y qué es exactamente lo que lo está causando. ¿Existe alguna manera inteligente de configurar un manejador de excepciones de nivel superior en algún lugar para ver qué lo está causando? No puedo replicar el problema yo mismo, pero algunos de mis usuarios beta ciertamente pueden hacerlo.
¿Cuál es una manera inteligente de manejar un problema de esta naturaleza?
Echa un vistazo a Crittercism . Va más allá de lo que está pidiendo aquí, ya que le permite obtener esta información para todos los usuarios que utilizan su aplicación, por lo que debería poder ver su propia falla.
También puede cargar el DYSM para su compilación particular y automáticamente simbolizará el bloqueo en su sitio web. Eso debería proporcionarle la traza de pila más clara sin estar conectado al depurador.
También es posible que desee asegurarse de que está configurado para romper las excepciones de Objective-C. En Xcode 4, en la pestaña de puntos de interrupción, puede agregar una excepción de punto de interrupción que rompa las excepciones de C ++ y Obj-C. Sin esto, la mayoría de los rastros de pila para excepciones lanzadas son bastante inútiles.
¡Buena suerte!
En XCode, siempre debe establecer un punto de corte global para objc_exception_throw
. Entonces usted (generalmente) obtiene un seguimiento de la pila mucho más significativo en cuanto a lo que realmente está tratando de lanzar una excepción.
Todavía puede obtener excepciones que se originan en código de temporizador u otros lugares sin su propio código en cualquier lugar de la traza, pero si observa la cadena de métodos generalmente puede averiguar en general de qué se trata la excepción (como enviar una notificación donde el objetivo se ha ido).
Otra opción para rastrear informes de fallas es Plausible CrashReporter , código fuente abierto para enviar automáticamente informes de fallas desde el campo.
También está CrashReporterDemo , otra opción de código abierto que es una combinación de Plausible CrashReporter y algún código de servidor para rastrear mejor los informes de fallos.
Y, por último, hay MacDevCrashReporter , un servicio que parece tener similitudes con iOSExceptional.com, sugerido en otra respuesta. No tengo idea de cuáles son sus términos de servicio, ya que no me he registrado para la versión beta. Definitivamente vale la pena consultar antes de entrar demasiado profundamente.
Parece que estás haciendo dos preguntas aquí: cómo configurar un controlador de excepción de nivel superior; y cómo lidiar con el problema de determinar cuál es la causa raíz.
La captura de la excepción se puede hacer de diferentes maneras, pero para esto, el mejor enfoque sería establecer un manejador de excepciones usando NSSetUncaughtExceptionHandler.
Cuando se genera una excepción en su aplicación, es manejada por un manejador de excepciones predeterminado. Este controlador no hace nada más que registrar un mensaje en la consola antes de que se cierre la aplicación. Puede anular esto estableciendo su propio manejador de excepciones personalizado usando la función indicada anteriormente. El mejor lugar para hacerlo sería en el método app delegate applicationDidFinishLaunching :.
- (void)applicationDidFinishLaunching:(UIApplication *)application
{
NSSetUncaughtExceptionHandler(&myExceptionHandler);
}
Una vez que haya configurado un controlador personalizado, querrá expandir el resultado predeterminado para ayudarlo a determinar cuál es la causa.
void myExceptionHandler(NSException *exception)
{
NSArray *stack = [exception callStackReturnAddresses];
NSLog(@"Stack trace: %@", stack);
}
Desafortunadamente, en comparación con OSX, el iPhone parece bastante limitado con respecto a la producción de un buen seguimiento de la pila. El código anterior producirá alguna salida aparentemente basura; sin embargo, puede ejecutar esta salida a través de la herramienta atos, y debería poder generar un seguimiento de pila útil a partir de ella.
Otra opción es seguir las instrucciones en este artículo que ayudarán a producir un buen seguimiento de pila automáticamente.
Como esto le va a pasar a los beta testers, es posible que deba retocar para que funcione.
Usted dice que no ha podido replicar el problema usted mismo, solo sus usuarios. En este caso, puede encontrar útil esta nota técnica de Apple:
https://developer.apple.com/library/content/technotes/tn2151/_index.html
ACTUALIZACIÓN : Si bien esta publicación todavía contiene información útil, algunos de los enlaces que contiene están irreversiblemente muertos. Se recomienda utilizar la información de this publicación alternativa.
Si planeas hacerlo por tu cuenta, podrías utilizar uno de estos enfoques
Enfoque1:
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
}
Enfoque2:
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
}
Nota:
En mi opinión, no intente enviar los detalles de la excepción al servidor en el momento de la falla, envíela cuando vuelva a iniciar la aplicación.
Si vas a usar NSUserDefaults para guardar los detalles de la excepción, entonces debes sincronizarlo en el momento de la falla, de lo contrario no se mantendrá.
El siguiente fragmento de código hace el trabajo.
- (void)applicationWillTerminate:(UIApplication *)application
{
[[NSUserDefaults standardUserDefaults]synchronize];
}
- Si prefiere guardarlo en sqlite db, entonces persiste sin necesidad de llamar a nada para persistir en el momento de estrellarse