Aprendiendo Objetive-C desarrollando para nuestro Iphone 3Gs

Puedes descargar el código fuente aquí

Aprendiendo Objetive-C desarrollando para nuestro Iphone 3Gs

Antecedentes

Julián, mi proveedor de mamparas y muebles de oficina ( http://www.provimobelmamparas.com ) me dijo hace meses que el iphone era el mejor teléfono que había tenido. Al principio no me parecía la mejor opción por el tamaño y fundamentalmente por no tener GPS. Yo era más bien de htc, y he tenido casi toda la gama. Al surgir el modelo 3Gs me he animado a coger uno y … es el mejor móvil que he tenido nunca. De hecho, otra vez me doy cuenta de que no tenía razón y de haberlo adquirido antes (incluso el modelo anterior) seguro que me hubiera interesado en su programación hace tiempo.

Lo bueno de no ser el primero es que ya hay multitud de ejemplos y libros sobre el tema por lo que “no pagas tanto las novatadas”. Además, desde que algo surge hasta que las empresas en España lo demanden creedme que pasan muchos meses.

He visto que algunos miembros de Autentia tienen también el htc con Android con unas sensaciones igual de buenas y … parece que hay una feroz competencia que dará mucho juego. Android tiene como ventaja de partida para nosotros los Javeros, que se programa en Java pero corremos también el peligro de entrar en el anti-patrón golden hammer (para un martillo de oro todos son clavos): No estaría bien elegir un dispositivo solamente porque usa el lenguaje que conocemos…

De momento me ha gustado la vistosidad de Iphone y como el lenguaje y entorno me es completamente nuevo estoy bastante picadillo porque veo decenas de posibles combinaciones con las aplicaciones empresariales que habitualmente nos piden. Aunque trabajemos habitualmente con JSF, Spring y cosas similares: interfaces ricos basados en Iphone o android y tirando de servicios Web y de nuestros componentes SOA, no parece descabellado. Adicionalmente para fortalecer el interés en aprender estas cosas, creo que el IPad (sobre todo una segunda generación futura que me arriesgaría a apostar sale a los pocos meses del primero) nos abrirán un mundo de posibilidades cambiando la percepción del interfaz de usuario… el tiempo lo dirá.

Aunque hay centenares de documentos cuanto te registras en la Web de Mac, empezar me ha requerido leerme un montón de cosas e incluso buscar por internet hasta cuadrar un poco los conceptos. En este tutorial pretendo presentar un poquito el lenguaje, un planteamiento básico de aplicación y las herramientas más comunes a la hora de desarrollar. Siendo egoísta, este tutorial me valdrá a mí para copiar y pegar en otros ejemplos porque al principio la notación se hace un poco extraña por el vicio de Java. Esto es como sacarte el carnet de conducir cuando has llevado desde los 15 años coches por el campo … tienes muchos vicios.

Por cierto, que no se os olvide visitar el tutorial anterior que tengo sobre esto. Seguro que os quedarán las cosas más claras. http://www.adictosaltrabajo.com/tutoriales/tutoriales.php?pagina=scrumiphone

Ejemplo a realizar

En mis cursos de UML y orientación a objetos, pongo siempre un ejemplo de lo simple que puede plantearse un sistema para pintar si tenemos claros los conceptos de polimorfismo.

El sistema consistirá en:
  • Una clase base que represente un objeto gráfico y defina los atributos comunes y los métodos a redefinir. Será CObjetoGrafic
  • Una clase derivada por cada objeto gráfico a pintar: Básicamente como un patrón estrategia. Serán CRectangulo y CElipse.
  • Un vista que controle los eventos del dedo (o ratón en el simulador) que diga donde pintar.
  • Unos botones que nos ayuden a definir si pintamos una elipse o un rectángulo.
  • Un controlador que reciba los eventos de los botones y establezca el tipo de elemento a pintar.
En principio parece muy fácil pero creedme que me ha llevado unas cuantas horas llegar al código adecuado. El mejor recurso ha sido mirar en los ejemplos del propio Iphone y sacar las referencias a la documentación.

Entorno


MacBook pro 13 pulgadas con pantalla auxiliar (es fundamental para trabajar cómodo).


Developer Information:
Version: 3.2 (10M2003)
Location: /Developer
Applications:
SDKs:

Creación del esqueleto del proyecto con el wizard de xcode

Creamos el proyecto basado en una vista y lo llamamos PintadorIphone

Examinamos el proyecto base

Vamos a insertar los iconos de la aplicación y el de por defecto.

Pintando el icono con Icon Composer.


Vamos a revisar las herramientas que vienen con el Kit de desarrollo para el Iphone y encontramos ésta: Icon Composer.

Copiamos un área de pantalla (una captura del simulador del Iphone con el programa terminado)

Y previsualizamos a ver cómo queda

Guardamos en el raíz de nuestro proyecto el icono. Le podemos dar a convertir a ICO

Vemos que baja un poquito el tamaño

Pero el Iphone necesita que el fichero tenga extensión png (lo he probado en .ico no funciona). El .ico lo abrimos con vista previa de Mac

Lo guardamos como png y ya está la conversión hecha.
También lo guardamos como Default.png para que nos valga de pantalla flash (aunque podría ir aquí publicidad o algo más vistoso)

No se nos debe olvidar ir a la carpeta de recursos y añadir los ficheros existentes

Ya tenemos el icono. Arrancamos el simulador del Iphone para comprobarlo

Añadiendo clase de vista

Nosotros vamos a hacer un programa que pinte atendiendo a los toques de la pantalla táctil de nuestro Iphone o simulando con el ratón en el emulador.
Al crear el proyecto desde el asistente, se crea un fichero que contiene nuestra vista. Ahora bien, como queremos redefinirla, necesitamos el código fuente de la clase derivada de UIView para, a través de polimorfismo de nuevo, redefinir los métodos deseados.

Nos vamos a xcode y en el menú file creamos un nuevo fichero (New Empty File)

El el combo desplegable elegimos la clase base UIView

Y le damos el nombre CAreaPintadoView. Es bueno ser coherente con la notación. De hecho muchas cosas en Objetive-C se basan en respetar convenciones en la nomenclatura.

Vamos a repetir el proceso para crear las clase: CObjetoGrafico, CRectangulo y CElipse

Vemos un ejemplo para ver que ahora derivan de NSObject, sobre todo nos importa CObjetoGrafico porque las demás las vamos a cambiar a mano para que dependan de ella.

También vamos a crear un prototipo simplemente por practicar. Es como un interfaz en Java. Sólo nos interesa el .h pero nos crea los dos ficheros. Por convención (mía) le hago que empiece por I mayúscula.

El punto m nos lo cargamos ya que no tiene sentido para nuestro protocolo.

En la siguiente pantalla podemos elegir borrar solo la referencia o mandar el fichero en sí a la papelera… como más os guste aunque yo prefiero borrarlo.

Establecer la clase manejadora de la vista

Ahora nos vamos al interfaz gráfico pinchando en cualquier fichero xib (mejor el controlador). Veremos que la vista (View a la izquierda) es de tipo UIView. Lo cambiamos a CAreaPintadoView.

Ahora tenemos que buscar un buen punto para hacer las inicializaciones de nuestras variables.
Redefinimos el método initWithCoder que es invocado al crear las vista. Es un buen punto para coger otros recursos que podamos tener almacenados dentro del fichero nib.

Manejarnos por la documentación y pdfs recomendados

Es vital saber saltar rápidamente a la documentación. Elegimos una palabra y pulsamos el botón derecho

Al principio la documentación puede parecer confusa … pero cuando te acostumbras a ella se maneja muy bien.

Yo pulso el botón de pdf a la derecha y me bajo toda la documentación en un solo documento pdf para leer en mi Kindle Dx.
Ésta es una colección de los pdfs que os recomiendo que encontréis.

Como véis, no me he aburrido pero todavía me queda una segunda y tercera lectura de cada uno de ellos.
Como se suele decir, para estar bella hay que sufrir… pues para saber, hay que estudiar.

Construir y ver lo que pasa en la consola.

El método NSLog vuelca en la consola mensajes que nos puede permitir ver de un modo cómodo qué pasa.

Además nos permite arrancar desde aquí la construcción y depuración

Si arrancáis el programa y no sale nada en los lo de la consola de xcode ¿qué puede ser?
Si estáis acostumbrados a C/C++ os vendrá a la idea de un modo inmediato el problema… problemas de linkado (perdonad el palabro).
En Bulid pulsamos Clean All Targets

Borramos todo

Volvemos a ejecutar y ya nos aparecen los comentarios. No se había enterado el builder de la asociación de una nueva vista (o eso parece).

Ahora vamos a revisar los resultados de la construcción. En el menú Build pulsamos Build Results

Vemos que de momento no tenemos problemas pero deberíamos en el futuro vigilar que tampoco tenemos Warnings.

Creando el comportamiento polimórfico

Queremos que todas las clases que pinten tengan la función pintate y que reciban el contexto de dibujo. Creamos un protocolo a tal fin llamado IPintable

Ahora vamos a crear la clase CObjetoGrafico que implementa pintable y que tiene las variables x,y,ancho y alto. También construimos un inicializador que por defecto empieza por init y que retorna self.
También proporcionamos un método auxiliar de traza para ver el estado de las variables sin recurrir al depurador.

Vemos el fuente.

Vemos ahora la implementación en el fichero .m

De momento todo sencillito

La clase CRectangulo hacemos que herede de CObjetoGrafico y hacemos lo mismo con CElipse.

Ver la relación de clases como modelo UML

Si pinchamos en la raíz del proyecto y damos a Design > Class Model > Quick Model

Podremos ver las relaciones de asociación, herencia, implementación que se van creando. Podemos hacerlo eligiendo otra clase cualquiera.

Creando botones en la vista y la acción ejecutora para cambiar el elemento a pintar.

Si volvemos al builder, arrastamos una barra de navegación y dos botones. No es que sea tal vez el control más adecuado pero queremos hacerlo muy simple.
Ponemos en los botones los títulos Elipse y Rect (ignorar que pone rectángulo o si cambiáis el título que sepáis que tiene impacto el el código posterior).

Si os fijáis a la derecha en Library, hemos elegido la clase PintadoriphoneViewController y hemos creado la acción botonPulsado que retorna un UIBarButtonItem (por defecto es un id).

Ahora deberíamos pinchar la rueda con engranajes de abajo y guardar los cambios en el controlador … pero como no me fío, lo vamos a hacer a mano (si queréis podéis ver el merge.. como en el tutorial anterior describía http://www.adictosaltrabajo.com/tutoriales/tutoriales.php?pagina=scrumiphone)

Hay que cambiar el .h para registrar la acción como método de tipo IBAction

Y también el .m

Donde vamos a pasar a la vista el tipo de elemento a pintar según se pulse el botón. Utilizaremos su propiedad pública “tipo”.Usaremos una única función por lo que en base al título del botón elegimos qué pintar.

Esto es un poco chapuzas porque hay más modos de hacerlo, como usar otras propiedades como el tag … pero así veis cómo se comparan cadenas.

Yo ahora cerraría el interfaz builder y lo volvería a abrir para que coja la acción.
Que no se os olvide que hay que asociar ahora los botones a la acción.

Con control pulsado o con el botón derecho elegimos el ratón y lo arrastramos a File´s Owner que nos mostrará las acciones disponibles.
Repetimos el proceso para los dos botones.

Incorporar el comportamiento de pintado

Ahora en la vista vamos a apoyarnos en un array de tipo NSMutableArray que nos permite añadir objetos dinámicamente sin clave.
Cuando el usuario pulse la pantalla nos quedaremos con la primera coordenada y la guardaremos en ítem e ítem.
Al dejar de tocar la pantalla cogeremos las coordenadas y restando a las anteriores calcularemos en ancho y alto.

Probamos cómo lo llevamos hasta ahora y en la ventana de build verificamos si hay algún error … a mi me falta un .h que añado.

Todo funciona de momento bien … y vemos nuestra pantalla con sus botones.

Aprender a depurar

A estas alturas deberíamos ya saber usar el depurador. Esto sí que es realmente intuitivo y parecido a otros lenguajes.
En el editor de código podemos pinchar a la izquierda (el área gris más gorda) para marcar un punto de interrupción.

En Run usamos el menú Debugger

Os recomiendo que activéis el ver el tipo de objetos (que suele ser fuente de errores)

Y vemos el tipo de cada elemento

Recordad que cuando pasan cosas raras… no es el entorno el que las hace “ERES TÚ”.
Para desactivar los puntos de parada o breakpoints en xcode podemos quitarlos arrastrando las flechitas fuera o a través del menú.

Pintando en la pantalla

La clave del código de pintado está en CAreaPintadoView

Al soltar el dedo (bueno esto queda un poco raro dicho) creamos una referencia de tipo objeto gráfico y lo inicializamos en la clase derivada hija.
Luego metemos el objeto en un array que recorreremos cada vez que sea necesario pintar.

Realmente esto no se haría así porque siempre es necesario usar doble-buffers: creas un contexto de tipo imagen que representa la pantalla, pintas en la imagen y luego vuelcas solo la imagen en la pantalla. Así es mucho más eficiente y se crean menos objetos.

Ahora añadimos el código de pintado en la clase CObjetoGrafico

Pintamos un rectángulo relleno. Como el comportamiento es polimorfo todo debería funcionar ya … aunque eligiendo elipses o rectángulos pintaría lo mismo.

Hacer una foto (snapshot) del código actual

Hemos llegado a un punto muy estable de código y sería una pena entrar en una espiral de violencia y perder fuentes. Siempre es conveniente usar un repositorio tipo CVS, subversion u otras opciones de pago.
A falta de uno, podemos hacerlo localmente creando un SnapShot.

Lo hacemos periódicamente y le ponemos un nombre

Podemos ver los cambios de código desde el Snapshot

Y compararlos e incluso recuperar los fuentes

Código en función polimorfa para pintar elipse

Ahora vamos a pintar las elipses

Realmente simple vamos a usar la misma función para pintar un área rectangular pero la vamos a alterar con patrones de transformación.

Y ya tenemos nuestro programa completo con cuadrados y circulitos pintamos un osito…

Indentar el código

Si habéis copiado o pegado código es bueno indentarlo.

Podemos ver cómo se comporta la aplicación si giramos el dispositivo. De momento no hemos hecho nada para que cambie el compotamiento.

Observar comportamiento de la memoria

Por último vamos a ver la herramienta para comprobar lagunas de memoria pero ésto tenemos todavía que tratarlo con cariño.

Se arranca un monitor donde va grabando el uso de la memoria según usamos nuestro programa. Ahora está parado y podemos pasar el botón por los puntos rojos para ver qué pasa.
Parece que el problema no es nuestro.

Me gusta más hacer las pruebas heurísticas, contar objetos y ver si se nos van quedando por medio.
Si os fijáis, a la izquierda, he elegido Allocation Lifespan/ Created & Still Living. Es decir, quiero ver el número de objetos vivos.

Si voy haciendo cosas y se me dispara … malo.

Pulsando elipse nos fijamos en la columna Living ..

Las herramientas de rendimiento son un mundo y podemos incorporar la monitorización de otros comportamientos (pulsando el botón Library arriba a la derecha)

Bueno, yo creo que por hoy ya está bien … seguiremos investigando sobre las tecnologías de desarrollo de nuestro Iphone, Objetive-C y las herramientas asociadas que creedme que son muchas.
Francamente me encanta (no había estado tan picado desde hace tiempo) . Espero poder compartir con vosotros este nuevo hobby que seguro que tiene miles de salidas en desarrollos empresariales.