Screencast

Bienvenido a la sexta parte de la serie Mobiletuts + Beginning iOS Development. Esta entrega cubrirá los fundamentos de depuración de Xcode. Incluirá una breve cantidad de teoría de depuración de software y una aplicación de práctica para demostrar el uso de puntos de interrupción y el depurador de Xcode. El artículo concluirá con algunos consejos generales y mejores prácticas, así como una lista de recursos útiles disponibles para que continúe su aprendizaje..

Screencast:

¿Problemas para ver los medios de comunicación arriba? Acceda a la versión completa y de alta calidad de este video en su formato original MOV.

Transcripción del tutorial:

En lugar de crear una nueva aplicación específicamente para este tutorial, tomé la aplicación FortuneCrunch que creamos en la segunda parte de esta serie y introduje una serie de errores diferentes en el código fuente.. Descarga BrokenCrunch, la versión rota de FortuneCrunch que acompaña a esta publicación., para seguir adelante mientras demuestro cómo usar las herramientas de depuración de Xcode.

Teoría de la depuración

Antes de comenzar a depurar BrokenCrunch, hablemos por un momento sobre la teoría de la depuración de software. En general, los errores de software (también conocidos como "errores") se pueden clasificar de la siguiente manera:

  • Errores en tiempo de compilación
  • Errores en tiempo de ejecución
  • Errores logicos

Errores en tiempo de compilación

Como su nombre lo indica, se produce un error en tiempo de compilación cuando intenta compilar el código fuente de su aplicación. En Xcode, esto sucede cuando seleccionas "Crear y ejecutar" o "Crear y depurar" para iniciar tu aplicación en el dispositivo o simulador. Cuando se encuentra un error en tiempo de compilación, literalmente evitará que su aplicación se inicie. Como veremos, los errores en esta categoría pueden ocurrir por sentencias de sintaxis sin sentido o incorrectas o por problemas que surgen en la fase de vinculación de la compilación de su aplicación. En general, los errores en tiempo de compilación son las más fáciles de resolver de las tres categorías, ya que el compilador generalmente emitirá un mensaje de error significativo o un mensaje de advertencia que lo alertará sobre la naturaleza del problema..

Errores en tiempo de ejecución

Los errores en tiempo de ejecución se producen después de que su aplicación se haya compilado y lanzado en el simulador o en un dispositivo. Un bloqueo de la aplicación o una pérdida de memoria que se produce como resultado de una administración deficiente de la memoria de objetos es un ejemplo de un error en tiempo de ejecución.

Errores logicos

Se produce un error lógico durante la fase de tiempo de ejecución de una aplicación y se traduce en un comportamiento inesperado o no deseado de la aplicación que entra en conflicto con el resultado previsto para el desarrollador del software o el participante del proyecto. Un buen ejemplo de un error lógico es una fórmula matemática que se ha implementado incorrectamente. Considere el teorema de Pitágoras:

Si un desarrollador de software implementó esta fórmula involuntariamente como:

El resultado sería un error lógico, pero lo más probable es que la aplicación no se bloquee. Eso es lo que hace que los errores lógicos sean tan peligrosos: la aplicación puede aparentemente ejecutarse "sin errores" al desarrollador, mientras que en realidad produce resultados no válidos o no deseados..

Depuración de BrokenCrunch

Con este conocimiento teórico en funcionamiento, abra BrokenCrunch y comencemos. Después de cargar nuestra aplicación de muestra, seleccione Construir> Construir y depurar. Notará que la aplicación no se inicia y que el compilador ha generado una serie de errores. Para ver los resultados del intento de compilación, seleccione Construir> Construir Resultados.

La selección de los errores enumerados lo llevará directamente a la línea de código donde se informa el error. Sin embargo, es importante tener en cuenta que la cantidad de errores que informa el compilador y los números de línea de esos errores deben considerarse como la "mejor estimación" de lo que está mal con su aplicación, no un pronunciamiento concluyente..

De hecho, un simple error de sintaxis puede dar como resultado que el compilador notifique varios errores que aparentemente no están relacionados con el problema. Como ejemplo, eche un vistazo a la "Se esperaba el corchete antes de 'setImage'" línea de error Si examina la línea en cuestión, debería encontrar que la sintaxis es perfecta. Como resultado, el problema aquí no está en la línea informada, sino en la línea justo encima de ella. Ves el problema?

los NSLog () declaración no terminó con un punto y coma. Esto significa que el compilador no sabe que pretendía terminar la línea después del último paréntesis, y está viendo todo desde NSLog hasta el corchete de cierre final y punto y coma después de UIControlStateNormal como una declaración.

Agrega el punto y coma para completar el NSLog declaración:

 NSLog (@ "CrunchCookie"); 

Guarde el archivo de origen y haga clic en "Crear y depurar" nuevamente. Dos de los tres errores mostrados originalmente deberían ser resueltos.

A continuación, seleccione la "No hay declaración de propiedad" error. Como puede ver, este error informa que la propiedad que estamos intentando sintetizar no existe. Para verificar esto, abra el archivo FortuneCrunchViewController.h donde debería haberse declarado la propiedad. Si examina la línea 17, la sintaxis es correcta, pero tenemos una discrepancia entre la propiedad que hemos declarado y la que estamos intentando sintetizar. Objective-C es un lenguaje que distingue entre mayúsculas y minúsculas, lo que significa que la 'C' en la cookie debe estar en mayúsculas para que coincida con la propiedad que estamos intentando sintetizar. Actualice la declaración de propiedad en el archivo de encabezado para leer:

 @ propiedad (no atómica, retener) IBOutlet * fortuneCookieButton; 

Guarda el archivo fuente y compila y depura una vez más. Esta vez, en lugar de abrir los resultados de construcción de Construir> Construir y depurar, simplemente haga clic en el icono de error en la esquina inferior derecha de Xcode.

Un paso adelante, cuatro pasos atrás. El error con respecto a la línea de propiedad de síntesis se ha ido, pero tenemos una lista de errores completamente nueva. Que pasó?

Este es un buen momento para tomar nota de las diferentes fases que se muestran en la ventana Resultados de la compilación:

Observe que tenemos una advertencia debajo de la sección "Compilar" de la salida de resultados de compilación. Esta es la misma sección en la que se informaron nuestros errores anteriores. Ahora que se han resuelto los tres errores anteriores, hemos podido pasar de la fase de compilación a la fase de vinculación de nuestra compilación de aplicaciones, y todos los errores nuevos son errores de vinculación. Cuando encuentra un error de vinculación, generalmente se debe a que está tratando de usar funciones de un marco que en realidad no ha incluido en su aplicación. En este caso, los resultados de la compilación hacen referencia a una función llamada _UIApplicationMain en el archivo main.o Main.o es la versión compilada del código de máquina de main.m. Echemos un vistazo al código fuente en ese archivo. En la línea 13 se puede ver una llamada a la función UIApplicationMain:

 int retVal = UIApplicationMain (argc, argv, nil, nil); 

UIApplicationMain es una función central para todas las aplicaciones de iOS, pero ¿cómo puede encontrar más información al respecto y averiguar en qué marco está incluido? Afortunadamente, el SDK de iOS viene con una gran documentación. Si mantiene presionado el botón de opción (o alt) y hace doble clic en el nombre de la función, lanzará un resumen de la documentación oficial del SDK de iOS que trata esta función. Haga clic en el icono de "libro" en la parte superior derecha para ver la documentación completa disponible. Puede ver que al hacerlo, se inició la documentación de referencia de funciones para el marco UIKit. Bingo, tenemos nuestro marco faltante. Sin embargo, antes de agregar el marco al proyecto, examinemos otro método que podría haber utilizado para determinar el origen de UIApplicationMain.

Cierra la ventana de documentación. Ahora, mantenga presionado el botón de comando y haga doble clic en el UIApplicationMain función. Ahora está buscando en la fuente de UIApplication.h, el archivo de declaración de encabezado que contiene el UIApplicationMain declaración de funciones. Si se desplaza a la parte superior de la ventana, verá que este archivo importa varios otros encabezados UIKit, y que el comentario en la parte superior incluye el nombre de marco "UIKit".

Continuemos con la resolución de estos errores de vinculación al incluir el marco UIKit. Para hacerlo, haga clic con el botón derecho o control, haga clic en la carpeta Marcos en el panel Grupos y archivos y seleccione añadir> marcos existentes. Encuentre el marco UIKit y haga clic en "Agregar". Para probar nuestro trabajo, seleccione Crear y Depurar de nuevo.

Como puede ver, el simulador se inició con éxito y podemos ver nuestra aplicación. Esto significa que hemos resuelto todos los errores de tiempo de compilación en nuestra aplicación.

Siga adelante y haga clic en la cookie de la fortuna ... como puede ver, al hacer clic en los resultados de la cookie en un error de tiempo de ejecución y la aplicación se ha bloqueado. El mensaje que se muestra en la parte inferior izquierda de la pantalla de Xcode no es muy útil, así que echemos un vistazo más de cerca abriendo la consola.

La consola muestra una pila de llamadas de lo que estaba ocurriendo en la ejecución de nuestro programa en el momento del bloqueo, así como una explicación más detallada: "Terminando la aplicación debido a una excepción no detectada ... FortuneCrunchViewController cookieCruncher: selector no reconocido enviado a la instancia". Este mensaje significa que nuestro botón está llamando al selector incorrecto para el evento que disparamos al hacer clic en la cookie. Dado que la interfaz para FortuneCrunch se construyó en Interface Builder, abrimos el archivo Interface Builder XIB para que "FortuneCrunchViewController" analice más de cerca.

Seleccione el botón de la cookie y haga clic en el control o haga clic con el botón derecho para ver una lista de acciones conectadas:

Puede ver que el evento Touch Up Inside hace referencia a un objetivo que no existe, indicado por el texto amarillo. Elimine el objetivo "cookieCruncher" que no existe y vuelva a conectar touchUpInside con el propietario del archivo seleccionando el objetivo "crunchCookie" que aparece en el menú desplegable. Guarde su trabajo en Interface Builder, vuelva a Xcode y reinicie la aplicación.

Al volver a hacer clic en la cookie de la fortuna se produce un error de tiempo de ejecución. Esta vez, el mensaje de la consola no es tan útil, solo muestra "EXC_BAD_ACCESS".

Eche un vistazo a los resultados de la compilación seleccionando Construir> Construir Resultados. ¿Notaste la advertencia antes? Las advertencias del compilador a menudo son una indicación de un posible error en el tiempo de ejecución, pero debido a que no hay nada incorrecto con la sintaxis real de la línea para la que se emite la advertencia, el compilador aún puede compilar la aplicación correctamente. Por supuesto, hay ocasiones en que una advertencia del compilador es un "indicador falso" y no dará como resultado un error en el tiempo de ejecución, sino más del 95% del tiempo, si el compilador ha emitido una advertencia de que está haciendo algo mal.

Haga clic en la advertencia para saltar a la línea en su código fuente donde ocurrió..

La advertencia se refiere a tipos de punteros incompatibles. ¿Ves el problema? El método imageNamed espera un objeto NSString, pero esta línea de código proporciona al método una cadena de estilo C literal. Agregue el símbolo “@” para hacer de esto una cadena de Objective-C:

 [fortuneCookieButton setImage: [UIImage imageNamed: @ "cookie-closed.png"] forState: UIControlStateNormal]; 

Guarda tu progreso y ejecuta la aplicación de nuevo..

Esta vez, cuando hace clic en la cookie de la fortuna, se encuentra un error lógico: la aplicación no falla y la etiqueta "Happy iPhone Hacking" aparece como se esperaba, pero la imagen de fondo permanece como la cookie cerrada..

Para solucionar esto, echemos un vistazo a la función responsable de la transición: (IBAction) crunchCookie. La línea 19 es responsable de cambiar la imagen de fondo y puede ver que está configurando la nueva imagen de fondo en "cookie-closed.png". Si echas un vistazo a las cookies cerradas en la carpeta Recursos, verás que esta es de hecho la misma imagen que se muestra cuando se carga la aplicación por primera vez. Necesitamos cambiar esa línea para pasar a "cookie-crunched.png":

 [fortuneCookieButton setImage: [UIImage imageNamed: @ "cookie-crunched.png"] forState: UIControlStateNormal]; 

Cree y ejecute la aplicación nuevamente ... y ahora, al tocar los resultados de las cookies en la imagen de fondo esperada con la etiqueta mostrada correctamente.

¡Felicidades! Acaba de pasar por el proceso de corregir errores de tiempo de compilación, errores de tiempo de ejecución y errores lógicos en una aplicación. Mientras tanto, apenas hemos aprovechado las poderosas herramientas de depuración disponibles con Xcode.

Para continuar con nuestra exploración de las herramientas de depuración más avanzadas disponibles, intentemos extender la aplicación FortuneCrunch para hacerla un poco más interesante. En lugar de mostrar la cadena estática "Happy iPhone Hacking!" Cada vez que se agrieta la cookie, construyamos una serie de múltiples NSString valores que podrían mostrarse.

Vuelva a Xcode y abra el archivo FortuneCrunchViewController.h. Agregue el siguiente miembro de datos:

 NSArray * fortunas; 

Esta matriz se utilizará para mantener nuestras cadenas de fortuna al azar.

Ahora, agregue la siguiente firma de método:

 -(NSString *) generateRandomFortune; 

Esta línea declarará un nuevo método en nuestra clase que se usará para seleccionar una fortuna aleatoria de nuestra matriz de fortunas.

A continuación, cambie a FortuneCrunchViewController.m. Dado que esta clase se iniciará desde nuestro archivo XIB, debemos anular la initWithCoder Método y asigne la matriz que declaramos en el archivo .h, inicializándola con algunas fortunas nuevas:

 -(id) initWithCoder: aDecoder self = [super initWithCoder: aDecoder]; if (self) fortunes = [[NSArray alloc] initWithObjects: @ "El que tira la tierra pierde terreno"., @ "Una boca cerrada no se junta con los pies". ¡Soy un prisionero en una panadería! ”, Nil];  devuélvete a ti mismo;  

Ahora que hemos creado una nueva NSArray, No te olvides de lanzarlo en el Dealloc método:

 -(void) dealloc [liberación de fortunas]; 

Vamos a pasar a la codificación de la genera aleatoriamente función:

 -(NSString *) generaRandomFortune int chosen_index = arc4random ()% 3 * 10; return [fortunes objectAtIndex: chosen_index];  

Estas líneas simplemente generan un nuevo número de índice aleatorio que usaremos para devolver la cadena de fortuna correspondiente.

Finalmente, modifique el crunchCookie método para usar una de nuestras fortunas al azar en lugar del texto estático "¡Feliz piratería de iPhone!":

 fortuneLabel.text = [self generateRandomFortune]; 

Crea y ejecuta la aplicación después de guardar estos cambios. Si hace clic en la cookie, creará un error de tiempo de ejecución. Para averiguar por qué sucede esto, vamos a utilizar el depurador de Xcode y los puntos de interrupción personalizados..

Un punto de interrupción es un indicador que señala a su aplicación que la ejecución del programa debe "pausarse" cuando se alcanza la línea con el punto de interrupción. La ejecución de su aplicación en "Modo de compilación y depuración" le permite utilizar puntos de interrupción. Para establecer un punto de interrupción, simplemente haga clic en el editor "margen" en la línea que desea activar un punto de interrupción. Para averiguar qué está pasando en nuestra aplicación, vamos a establecer nuestro punto de ruptura en el NSLog línea, justo después de que el método crunchCookie se llama:

Construya y depure la aplicación con este nuevo punto de interrupción en su lugar.

Después de que se cargue la aplicación, haga clic en la cookie. Si mira en la parte inferior izquierda de Xcode, verá el mensaje de estado "Detenido en el punto de interrupción 1". Esto significa que el depurador ha detenido exitosamente la ejecución del programa en el punto de interrupción que estableció. También notará que una flecha roja indica la línea actual de ejecución donde el depurador ha "pausado" el programa.

Entonces, ¿qué puedes hacer con el depurador? Más de lo que se puede cubrir en un solo tutorial. Sin embargo, hay tres acciones fundamentales que puede tomar en este punto: dar un paso adelante, entrar y salir. Todas estas opciones están disponibles para usted desde la barra de menú del depurador de código.

Si presiona el botón "pasar sobre" en el menú del depurador de códigos, notará que la ejecución del programa continúa en la siguiente línea. "Paso a paso" simplemente continuará la ejecución una línea a la vez dentro del método actual, pero no seguirá la ejecución de su código si se desvía hacia otro método. Si desea seguir realmente la ejecución del código en otras llamadas de método en su código, deberá utilizar el botón "entrar".

Como puede ver, el paso a la realidad nos ha llevado a la genera aleatoriamente Método, que es exactamente lo que queremos. Haga clic en "Pasar de nuevo" de nuevo para ver qué sucede cuando arc4random () se llama. ¿No sería bueno si supiéramos qué se ha definido la variable chosen_index? Afortunadamente, podemos! Una de las mejores características de usar el depurador es la capacidad de simplemente pasar el mouse sobre las variables para ver rápidamente su valor.

Claramente, el índice_eleccionado El valor es mucho mayor que la longitud de nuestra matriz. A diferencia de otros lenguajes de programación, la función de aleatorización que estamos utilizando devolverá un entero, por lo que no es necesario convertir de un decimal a un entero multiplicando el valor por 10. Actualice la línea para leer:

 int chosen_index = arc4random ()% 3; 

Hemos terminado de hacer modificaciones a esta función, así que use el botón “Salir” para salir de esta subfunción y volver a crunchCookie. Tenga en cuenta que aunque no lo vimos, el resto de la función se ejecutó normalmente..

Finalmente, tome nota del botón "Activar / Desactivar" del punto de interrupción y el botón "Continuar ejecución" en la barra de menú del depurador de códigos. "Continuar ejecución" simplemente permitirá que la ejecución del programa continúe normalmente. Puedes considerarlo como el botón de "pausa". Sigue adelante y presiona esto ahora.

Antes de pasar a desactivar los puntos de interrupción, hay un problema más que abordar: lo que acaba de experimentar se denomina "depurador de códigos". Es muy potente, pero también hay otros dos modos de depuración disponibles: la ventana completa del depurador y la perspectiva del mini depurador.

Para acceder a la ventana completa del depurador, haga clic en el ícono "depurar" en la barra de menú del depurador en el código. Esta ventana tiene mucha más información que el depurador en el código. A la izquierda, tiene un seguimiento de pila que muestra el contexto de ejecución del programa (también tiene la capacidad de seleccionar cualquiera de los subprocesos creados actualmente). A la derecha, puede ver una visualización rápida de las diversas variables que se encuentran actualmente en la memoria. Seleccionar una firma de pila de llamadas diferente cambiará su vista en el Depurador. Puede cambiar el diseño de la ventana del depurador yendo a Ejecutar> Visualización del depurador.

Finalmente, el mini-depurador es otra perspectiva de depuración disponible para usted. Raramente utilizo esta perspectiva, pero está disponible para usted desde Ejecutar> Mini-Depurador.

Como acabamos de resolver el error introducido en nuestro código de fortuna aleatorio, ya no necesitamos que el depurador esté encendido. Desactivar los puntos de interrupción. Sin embargo, antes de volver a compilar la aplicación, ajustemos el tamaño de fuente de nuestra etiqueta de fortuna.

Abra Interface Builder, seleccione la etiqueta y cambie la fuente en el Inspector a Arial Black, 9 puntos, y luego seleccione la casilla "Ajustar para encajar" y cambie el tamaño de fuente mínimo a 6 puntos. Ahora, construye y ejecuta nuestro proyecto de nuevo..

Voila! Nuestra aplicación ahora funciona como pretendíamos.

Consejos y trucos de depuración

Ahora que ya conoces los conceptos básicos del uso del depurador en Xcode, considera aplicar las siguientes pautas en tu flujo de trabajo de desarrollo diario:

Prueba tanto en el simulador como en un dispositivo físico

Si bien el simulador es un método útil para probar una aplicación durante la fase de desarrollo de su producto, no es un reemplazo para la prueba en un dispositivo físico. Esto se debe a que el simulador y un dispositivo iOS difieren en aspectos importantes y fundamentales. Por ejemplo, el simulador obviamente se ejecuta dentro de OS X, y el sistema de archivos en OS X no distingue entre mayúsculas y minúsculas. Sin embargo, el sistema de archivos en iOS distingue entre mayúsculas y minúsculas. Así que refiriéndose al archivo. cookie-CRUNChed.png, en lugar de cookie-crunch.png, funcionará bien en el simulador pero fallará en un dispositivo iOS real. Otra consideración clave es que el simulador tiene mucha más memoria disponible que un dispositivo real, y este hecho a menudo tendrá un gran impacto en la experiencia del usuario. Finalmente, no todas las aplicaciones predeterminadas que vienen con iOS están disponibles en el simulador, incluidos los programas Maps y App Store. Esto significa que no podrá probar el código que genera indicaciones de manejo con la aplicación Maps o las aplicaciones de promoción cruzada en la App Store en el simulador. Estas son solo algunas de las diferencias que existen. Recomiendo realizar pruebas en tantos dispositivos iOS físicos como sea posible con tantas versiones específicas de iOS como sea posible.

Utilice el analizador estático Clang

El analizador estático Clang es una herramienta especial de análisis estático C / Objective-C que se envía con Xcode. Esta herramienta es capaz de analizar su código en busca de errores o inconsistencias que de otra manera podrían pasar desapercibidas..

Si bien los detalles de cómo funciona el analizador están fuera del alcance de este artículo, afortunadamente, su uso es muy sencillo. Para realizar un análisis estático de su código, simplemente seleccione Construir> Construir y Analizar desde el menú de compilación de Xcode.

Si desea obtener más información sobre cómo funciona la opción "Crear y analizar", puede leer en línea sobre el análisis de código estático y el analizador de estática Clang..

Establecer un punto de interrupción global en objc_exception_throw

En este tutorial, aprendimos cómo funcionan los puntos de interrupción al establecer puntos de interrupción específicos del proyecto en nuestro código. Además de los puntos de interrupción específicos del proyecto, Xcode también le permitirá establecer puntos de interrupción "globales" que se aplicarán a todos los proyectos de iOS que cree en Xcode. Configuración de un punto de interrupción global en objc_exception_throw le permitirá iniciar automáticamente el depurador cuando se produzca una excepción (un tipo de error de tiempo de ejecución). Para obtener más información sobre los beneficios de este enfoque y cómo implementarlo en el código, consulte la Sugerencia rápida de iOS sobre objc_exception_throw y los puntos de interrupción globales.

Tratar las advertencias como errores

Como se mencionó anteriormente en este tutorial, la gran mayoría de las advertencias de compilación que se emiten deben resolverse antes de iniciar su proyecto, y realmente nunca debería haber un escenario cuando el código que genera las advertencias de compilación debe permanecerá sin cambios para que una aplicación funcione correctamente. En consecuencia, algunos programadores recomiendan tratar todas las advertencias del compilador como errores, lo que obliga a que se resuelvan como parte del proceso de desarrollo normal..

Para todos menos unos pocos casos marginales, apoyo esta idea y Xcode hace que sea fácil de implementar. Ir Proyecto> Editar configuración del proyecto y luego seleccione la pestaña Construir. Escribe "Tratar la advertencia" en la barra de búsqueda y verás un valor booleano llamado "Tratar las advertencias como errores". Marca esta casilla para habilitar la función.

Construir y validar antes de la presentación de App Store

Otro paso que puede tomar para aumentar las probabilidades de que su aplicación sea aceptada la primera vez que la envíe a iTunes Store es habilitar el indicador "Crear y validar" en la configuración de compilación del proyecto. Escriba "validar" en el cuadro de búsqueda de la pestaña de compilación de configuración del proyecto y luego seleccione "Validar producto de compilación". Esta opción ejecutará algunas de las pruebas realizadas por los revisores de Apple, lo que le permitirá anticiparse a un rechazo de App Store. Debe tenerse en cuenta que marcar esta casilla no es una garantía de que su aplicación pase la revisión de la App Store, pero es mejor que nada.

Herramientas de depuración adicionales

Además de la consola, los resultados de la compilación y el depurador, existen otras excelentes herramientas de depuración y optimización que debería conocer en sus esfuerzos de desarrollo. Para obtener más información, consulte la documentación de las siguientes herramientas:

  • Instrumentos
  • Tiburón
  • Control superior / giro
  • BigTop

Conclusión

Este ha sido un torbellino de depuración con el SDK de iOS. Todavía se puede hacer mucho más, pero espero que esta lección haya sido suficiente para ayudarlo a resolver más rápidamente los errores en sus propias aplicaciones y escribir mejor software. Si desea obtener más información sobre algunas de las herramientas avanzadas que se analizan en este tutorial, como Instruments, Shark o SpinControl, o si desea obtener más información sobre la depuración en general, deje un comentario a continuación y avíseme.!