1. ¡Bienvenido al curso de programación para iOS!
Este documento es un tutorial (paso a paso) de la demostración realizada en clase.
Usted necesitará este tutorial para hacer tu primera tarea.
Las burbujas amarillas le dirán lo
que tiene que hacer
Las burbujas verdes son solo para Las burbujas rojas son cosas
“información” “importantes”
Las burbujas verdes con texto pequeño
son “notas menores”
Universidad Galileo
2. Pantalla de bienvenida
de Xcode 4
Inicia Xcode 4 y haz click
en proyecto nuevo
Tus proyectos creados
aparecerán aquí
3. Click en esta plantilla de aplicaciones de iOS.
Se crea una simple aplicación MVC.
Con Xcode 4 se
puede desarrollar
aplicaciones tanto
para iOS y Mac OSX
Estos botones se usan para
seleccionar una plantilla que
Xcode 4 utiliza para generar
código para que puedas
empezar.
4. Nuestra primera aplicación va
a ser una Calculadora RPN
El nombre de nuestro proyecto va a ser “Calculator”
Una calculadora RPN saca sus operadores
de un stack (una pila). Los usuarios deben
escribir un número, presionar Enter, luego
otro número y otra vez Enter, después el Ingresa edu.galileo.TuNombre
operando entre esos dos números.
Usar tu dirección de
correo electrónico es una buena
forma de obtener un
Este campo se utiliza para identificar identificador único.
de forma única tu aplicación.
Estos campos nos sirven para
describir el proyecto.
5. No queremos que los nombres
de las clases generadas por la
plantilla sean demasiado
Escriba “Calculator” como prefijo para
genéricos. Es por eso que
las clases que esta plantilla va a generar
especificamos este prefijo.
para nosotros
Nuestra primera aplicación va
a ser para iPhone (no iPad).
Por lo menos para empezar.
Por lo general se utiliza el nombre de la aplicación en este prefijo.
De hecho, en las versiones anteriores de Xcode se realiza esto
Una aplicación Universal funciona tanto en iPhone y automáticamente se quiera o no.
iPad. En una aplicación Universal, el iPad y el iPhone
cada uno tiene su diseño de interfaz de usuario propia
(ya que tienen un UI diferente). Xcode proporciona
herramientas para el diseño de de dos Interfaces de
usuario diferentes en la misma aplicación.
6. Los Storyboard
(guiones gráficos) son ARC es una fantástica actualización
una nueva (iOS 5) para el compilador (en iOS 5), que
manera de organizar las hace que se genere todo el código
vistas de sus MVC’s. necesario para gestionar la asignación
Vamos a usarlos. de memoria de los objetos.
Definitivamente, lo vamos a utilizar.
Puede ser que tengamos
tiempo cerca del final del curso
para cubrir las pruebas unitarias,
pero no vamos a crear ninguna
para nuestra primera aplicación.
Click en siguiente (Next)
7. Xcode quiere saber donde almacenar el
directorio de este proyecto.
Directorio
Personal (Home)
La carpeta “Developer” dentro del directorio home.
No hay proyectos actualmente.
Vaya al directorio llamado “Developer” en su
directorio personal (crear si es necesario), a
continuación, haga click en Create para crear el
directorio del proyecto dentro de ~/Developer
Si usted no tiene una carpeta de desarrollo
(Developer) en su directorio personal, se
puede crear con este botón de New Folder.
Definitivamente vamos a estar cubriendo control de código fuente en este curso.
Pero no para este primer proyecto, así que no seleccione esta opción.
8. Felicitaciones, usted ha creado su
primera aplicación para iOS!
Usted probablemente querrá hacer esta ventana lo más grande posible.
Xcode se puede utilizar a pantalla completa.
Hay un montón de cosas en esta ventana, pero no vamos a
estar cubriendo nada de eso en nuestra primera aplicación.
9. La plantilla de vista de aplicación (View Application) única que
elegimos al principio ha creado un simple MVC para nosotros.
Nuestra Vista del MVC se encuentra dentro de MainStoryboard.storyboard.
CalculatorViewController.[mh] es el código del controlador del MVC.
Tendremos que crear nuestro Modelo del MVC más tarde.
Vamos a abrir y mirar nuestra Vista del MVC,
No te preocupes por haga click en MainStoryboard.storyboard.
CalculatorAppDelegate.
[mh] para este proyecto.
10. Esta es nuestra Vista del MVC.
Por supuesto comienza en blanco.
Haga click en “Run” para ejecutar la aplicación
Esto debe estar seleccionado.
Si no es así, eso explicaría
porque no estas viendo la Vista
del MVC.
11. Xcode esta construyendo (Building) la aplicación.
Este es el “Selector de Sistema” (Scheme Chooser).
Te permite elegir dónde ejecutar la aplicación.
Por ejemplo, el simulador de iPhone, el simulador de
iPad o en un dispositivo real.
Si mantiene pulsado del botón de Run
(ejecutar) otras opciones de ejecución estarán
disponibles, pero solo estaremos utilizando la
ejecución sencilla por el momento.
12. Esperamos que vea esto.
Esta barra que aparece en la parte de inferior forma parte de la
consola y el debugger (depurador). Vamos a hablar de esto más tarde.
13. Simulador de iOS en iPhone.
Felicidades de nuevo!
Ha ejecutado su primera aplicación de iOS en el simulador de iOS
Va a utilizar el simulador para la mayoría (pero no todas) de sus tareas.
Cuando ya haya visto todo lo genial de esto haga
click en el botón detener (Stop).
Vista en blanco del MVC.
Este botón muestra o oculta la consola/depurador.
Puede hacer click en el si lo desea.
14. Este es el Navegador
Muestra los archivos
Este es el Esquema
de su proyecto de
del Documento
forma jerárquica de
acuerdo a las
Contiene una vista
carpetas. La
jerárquica,
disposición de las
representativa de
carpetas es
todos los objetos de
conceptual, no
la vista del MVC.
necesariamente
Nosotros no lo vamos
coinciden con lo que
a utilizar durante esta Si pasa el ratón sobre este botón vera una descripción diciendo
esta en el sistema de
primera aplicación. que oculta el esquema del documento (Hide Document Outline).
archivos.
Sin embargo, es Haga click en el botón para ocultar el esquema del documento.
Esta área también
posible que desee
puede mostrar los
verlo de vez en
símbolos, resultados
cuando a medida que
de búsqueda,
avanzamos en esta
breakpoints,
aplicación.
problemas, etc. (Ver
iconos en la parte
superior).
15. Ahora necesitamos ver a nuestro Controlador del MVC.
Pero aun queremos tener nuestra Vista del MVC en pantalla.
La forma de tener dos cosas en la pantalla a la vez es usar el
editor asistente.
Se muestra/oculta usando este icono.
Logs (Registros)
Cada vez que se genera/ejecuta se guarda un registro.
Acceda a los antiguos aquí.
Problemas
Advertencias o errores del compilador, etc.
Haga click para ver el Editor Asistente.
Buscar
Busca/remplaza archivos en su proyecto. Cuando una Vista del MVC se está mostrando,
aparecerá por defecto el controlador de la Vista.
Eso es exactamente lo que queremos.
Puede ver el
esquema del
documento en
cualquier momento
haciendo click en este
botón de nuevo.
16. Este es el encabezado (.h) de archivo para nuestro Controlador del MVC.
Contiene sus métodos y propiedades públicas y también define su superclase
(todos los controladores de iOS heredan de UIViewController).
Tenga en cuenta la sintaxis @interface - @end
UIKit.h importa todas las clases de interfaz de
usuario de iOS.
#import es como #include, pero mejor.
No necesitamos el navegador en el extremo
izquierdo, así que vamos a ocultarlo
desactivando este botón
17. Vamos a hacer aún más espacio para nuestro
código de la derecha arrastrando la barra
central para la izquierda.
18. Vamos a empezar a construir la interfaz de usuario en nuestra Vista del MVC.
Para ello, vamos a necesitar una etiqueta de texto y algunos botones.
Tenemos los de la biblioteca de objetos en el área de Utilidades (que se activa a
través de este botón).
Haz click para abrir el área de Utilidades.
19. Utilidades
La parte superior de
esta zona muestra la
información
(identidad, atributos,
ayuda, conexiones,
dimensiones) sobre
el elemento
seleccionado
actualmente (archivo,
objeto, etc.) a la
izquierda.
En el fondo es una
biblioteca de
elementos (objetos,
fragmentos de
código, plantillas de
archivos).
20. La barra superior estará de un gris más oscuro si el File Inspector
elemento seleccionado se encuentra en la mitad del Muestra información sobre el
editor Asistente. archivo que contiene el ítem
seleccionado.
Ayuda rápida
Si el elemento seleccionado a la
izquierda tiene alguna referencia,
esta documentación muestra un
resumen de la misma.
Biblioteca Multimedia
Imágenes, sonidos, etc.
Biblioteca de Objetos
Botones, campos de texto, controladores, etc.
Biblioteca de plantillas de archivo
Plantillas para guiones gráficos, clases, etc.
Biblioteca de fragmento de código
Fragmentos de código para tareas comunes.
Haga click en la biblioteca de objetos (que podría ya estar seleccionada)
21. De vez en cuando, Xcode puede
descargar alguna documentación
para usted en background.
Haga click en la Vista para seleccionarla
Algunos de los objetos (Los que se
pueden arrastrar al la vista) deben
aparecer en la biblioteca de objetos.
22. Vamos a empezar a construir la Vista de nuestra
calculadora arrastrando una etiqueta (Label) de
texto para el display de la calculadora.
Arrastre una etiqueta (Label) desde la
biblioteca de objetos a su Vista
Observara unas lineas discontinuas azules
que Xcode muestra a medida que arrastra el
objeto que le ayudara a colocarlo bien.
Los Label’s (etiquetas) son instancias
de la clase UILabel en el SDK de iOS
23. Haga click en el inspector de atributos
Usted debe ver los atributos de la etiqueta que acaba de crear
Usted puede modificar el tamaño que
tiene el Label en la vista.
Eso es lo que haremos a continuación.
24. Agarra la esquina inferior derecha
de la etiqueta y cambia el tamaño
Usa las lineas de color azul para
elegir un buen tamaño
Este pequeño indicador te mostrará
el tamaño exacto mientras lo estés
cambiando.
25. Los números en la pantalla de una calculadora no
se alinean a la izquierda, así que vamos a cambiar
la alineación del texto en la etiqueta de la pantalla
haciendo click en este botón del inspector.
Note que los cambios en el inspector
se reflejan inmediatamente en la Vista
26. También vamos a hacer la letra mas grande
Haga click en esta pequeña flecha hacia arriba
para aumentar el tamaño.
24 puntos Helvetica esta bien.
27. No queremos que nuestra calculadora inicie con la
palabra “Label” en la pantalla!
Por lo cual haga doble click en la etiqueta para
ponerlo en un estado de edición...
28. También se puede editar el texto de la
etiqueta en el inspector de atributos.
... a continuación escriba 0
29. Debe tener este aspecto
después de escribir 0
Importante!
El Controlador debe ser capaz de hablar con la Vista.
Por ejemplo, en este caso es necesario ser capaz de actualizar la pantalla
conforme los dígitos se presionan (y con los resultados de las operaciones).
Podemos hacer esta conexión entre el Controlador y la Vista directamente
con el mouse...
30. PRESIONE CONTROL y arrastre con el ratón la etiqueta de texto directamente al código
de nuestro controlador
Si no mantiene presionada la tecla control esto no funcionara.
Note que a medida que ctrl-arrastre sobre su código un
indicador aparecerá, por lo cual es mas fácil ver
exactamente en que parte del archivo estará el código de
31. Xcode ahora quiere saber que tipo de conexión queremos hacer
entre el Controlador y este objeto de la Vista.
En este caso ya esta la conexión en un Outlet.
Un Outlet es solo una característica del Controlador a través del
cual podemos hablar con un elemento en la Vista.
El destino de esta conexión es
nuestro Controlador ya que es
donde ctrl-arrastramos. Importante!
32. Vamos a llamar a esta pantalla display (esta
es la pantalla de la calculadora)
33. Importante!
Un outlet es un puntero a un objeto (un UILabel en este caso).
Un puntero strong (fuerte) significa que el UILabel se quedará hasta que se termine de usar el UILabel.
Un puntero weak (debil) significa que el UILabel solo se quedará mientras alguien más tiene un puntero strong hacia el.
Cuando nadie más tiene un puntero strong a un objeto que tenemos un puntero weak, ese objeto va a desaparecer y
nuestro puntero a el se borrará y no vamos a ser capaces de hablar con el (porque ya se habrá ido).
Como esta ventana ya tiene un puntero strong al UILabel, weak es una buena opción aqui.
34. Haz click en Connect (conectar) para crear una
propiedad (llamada display) en nuestro Controlador
que apunte a este UILabel en nuestra Vista
35. Voilà!
Xcode ha añadido una propiedad (@property) a nuestro
Controlador del MVC que es un puntero a un objeto UILabel.
También ha enganchado esta @property hasta el texto de
nuestro Label que arrastramos a nuestra Vista del MVC.
Importante!
display es el nombre de esta @property
UILabel * es el tipo de esta @property
(que significa “puntero a un objeto UILabel”)
Por lo tanto, cada vez que IBOutlet es una palabra que Xcode coloca aquí para
nuestro Controlador envía que pueda recordar que es una propiedad de salida.
En realidad no significa nada para el compilador.
mensajes a la @property
display, va a hablar a una
instancia de UILabel.
nonatomic significa “not thread-safe” -- hablaremos sobre esto después.
Esta propiedad no dice que es un puntero débil (weak).
36. Pase el mouse sobre (pase el mouse por
encima, no haga click en el) este pequeño icono
para ver donde esta @property esta conectada.
Note como se resalta el Label.
Se resalta el Label
37. Si hace click en este icono, se mostrará una
lista de todos los storyboards que estan
enganchados a esta propiedad.
Recuerde que dijimos que una sola aplicación
puede soportar múltiples interfaces de usuario
(por ejemplo iPhone y iPad).
Esto lo hace con varios storyboards.
Así es como un Controlador es capaz de
soportar las diferentes interfaces de usuario.
38. Pasando el mouse sobre el único elemento de
la lista se selecciona el label también.
39. Nuestra cabecera del controlador (header)
Nuestra implementación del controlador
Haga click y selección la implementación de
nuestro Controlador para que podamos agregar
más conexiones con nuestra Vista
40. Bienvenido a la implementación
(archivo .m) de tu Controlador!
Note que en @implementation no se especifica la superclase.
Solo en @interface en el archivo .h (header) se hace eso.
Este @synthesize es muy importante.
Lo vamos a cubrir dentro de un momento.
41. Xcode ha añadido código que no es necesario
para esta aplicación, así que selecciona todo
después de @synthesize display ...
43. Importante!
Note que Xcode agrega automáticamente @synthesize a la implementación de nuestro
Controlador cuando se creó la @property display (que lo hizo cuando ctrl-arrastro para crear el
outlet display).
@synthesize crea dos métodos (display and setDisplay:).
El método setDisplay: es usado por iOS para enganchar el UILabel hasta la @property display en
tiempo de ejecución (es decir establecer el valor del puntero).
El método display es utilizado por nosotros para obtener el puntero del UILabel para que
podamos enviar mensajes al UILabel.
@synthesize también crea una variable de instancia para almacenar este puntero.
Vamos a hacer más cosas con las @propertys después.
44. Ahora es tiempo de agregar a nuestra calculadora los botones del teclado
Arrastre un botón (Round Rect Button)
de la biblioteca de objetos a la Vista
45. Modifique el tamaño del botón hasta
que tenga un tamaño de 64 puntos de
ancho, ese tamaño funcionara bien
Importante!
Recuerde que el término outlet se refiere a una @property a través del cual
podemos enviar mensajes a algo en nuestra Vista desde el Controlador
(display es un outlet).
Importante!
Usamos el término action (acción) cuando un método va a enviar un
mensaje a partir de un objeto en la Vista a nuestro Controlador cuando algo
pasa en la interfaz de usuario.
Así que nuestro siguiente paso es especificar la acción que este Importante!
UIButton va a enviar a nuestro controlador cuando el usuario lo toca.
46. PRESIONE CTRL mientras arrastra con el mouse una linea desde
el botón hasta la zona del texto de su código
Xcode sabe que desea crear una acción en
el lugar donde ctrl-arrastro en lugar de un
outlet.
47. Al soltar el mouse, este cuadro de
dialogo de la “acción” aparecerá.
Este es el nombre del
método de la acción
Este es el objeto al que se envió el
mensaje de acción a nuestro Controlador.
Este es el tipo de evento
táctil que hará que esta
acción sea enviada.
Especifica el formato del mensaje
(ampliaremos más adelante).
48. Introduzca digitPressed como el nombre
de la acción (lo cual tiene sentido ya que
este botón va a ser un botón de dígitos en
el teclado de la calculadora)
Puede dejar el resto de los campos
(los valores por defecto están muy bien para este botón).
A continuación presione
Connect (conectar)
49. Esta es su primera declaración de
un método en Objective-C!
IBAction es exactamente lo mismo que void (es
decir, este método no devuelve ningún valor).
Xcode lo usa en vez de void solo para
diferenciar un método de acción de otros
métodos con una forma similar.
Cada argumento (como sender) en un método
de Objective-C es precedido por una parte del id es el tipo del argumento sender.
nombre del método (como digitPressed) y dos id significa “puntero a un objeto de cualquier
puntos.
Importante!
Puede que se sorprenda al no leer “id *”.
Pero eso no tendría ningún sentido porque el tipo “id” es
ya un puntero de modo que “id *” sería un puntero a un
puntero.
id no significa “objeto de cualquier clase”, significa
“puntero a un objeto de cualquier clase”.
50. Ahora cada vez que se toca este botón digitPressed: va a ser llamado en nuestro
controlador con el propio UIButton sender como argumento del mensaje.
Importante!
Similar a un outlet, puede pasar el mouse
sobre este icono y espere a ver cual es el
objeto(s) en la Vista que envía este mensaje.
Observe como el botón se resalta.
51. id es un tipo muy especial.
Hay algunos momentos en los que desean
utilizarlo, ya sea porque permite cualquier
clase de objeto que se pasa a un método
(poco común) o porque la clase del objeto
es “opaca” (es una “cookie”).
Importante!
Pero ninguno de esos casos se aplica aquí.
En este caso sabemos que el sender de
digitPressed: va a ser un UIButton.
Por lo tanto vamos a cambiar tipo como un
“puntero a un UIButton” en lugar de un “puntero a
un objeto de cualquier clase”.
Importante!
52. Remplace id por “UIButton *”.
A utilizar UIButton * en lugar de id se le llama “static typing” (tipos estáticos).
Static typing es puramente una cosa del compilador.
No tiene efecto en lo que sucede en tiempo de ejecución.
El compilador solo va a generar mejores advertencias si se intenta escribir un código
que envía un mensaje con un sender que un UIButton no reconoce.
Si usted envía un mensaje con un sender que no se reconoce, el programa fallara
(crash), independientemente de la declaración de tipos estáticos del sender.
Importante!
53. Necesitamos más botones!
Copie y pegue nuestro primer
botón para hacer otro botón
El botón copíado enviará la misma acción
(digitPressed:) que el original.
54. Ahora mueva el botón copiado para alinearlo con el original (las lineas
discontinuas azules son impresionantes aquí)
60. Haga esto para todos los botones.
Ahora el teclado debería tener este
aspecto
61. Cuando se inicia la construcción de interfaces de usuario más complicadas es muy
importante ser capaz de ver sus conexiones de outlets o acciones.
Usted puede hacer esto haciendo click derecho sobre cualquier objeto en la Vista del MVC.
Haz click derecho en el botón del número 9
El inspector de conexión
mostrará todo esto también
Puedes realizar conexiones
ctrl-arrastrando estos
pequeños círculos también
Esto muestra que el botón 9 envía
digitPressed: al CalculatorViewController
Puede (su Controlador), cuando hay un toque
desconectar
esta acción
haciendo click
en la pequeña x
No hay outlets que
apunten a este botón
62. Pase el mouse sobre esta conexión y verá que toda
la Vista se resaltará (es su manera de mostrar que
La Vista se resalta
este botón envía el mensaje al Controlador)
63. Haz click derecho sobre el display
Este UILabel está conectado al
display a través del outlet a su
controlador
64. Una vez más, pasando el mouse sobre esta
conexión verá que toda la Vista se resaltará (es su
manera de mostrar que este display esta conectado
al Controlador)
La Vista se resalta
otra vez
65. Haz click derecho
en este icono que
representa el ...ahora pasa el mouse
Controlador... sobre la entrada del botón 4
Note que el botón 4 sobresale sobre los demás
66. ...Pasa el ratón sobre este outlet
Note que la etiqueta del display se resalta
No te preocupes por el
outlet de la Vista, lo
explicaremos más
adelante en este curso
67. Este icono de color gris significa que este archivo no se ha guardado.
Si usted tiene una advertencia aquí es porque digitPressed:
no esta implementada, probablemente es porque usted
necesita guardar el archivo CalculatorViewController.m
68. No vamos a necesitar el área de
Utilidades por un tiempo, por lo que
vamos a cerrarlo
Es el momento de escribir el código dentro de
digitPressed: que se ejecuta cada vez que
cualquiera de estos botones se toca...
69. Vamos a empezar declarando una variable local llamada digit que
será del tipo “puntero a un objeto NSString”
Si, podríamos poner id digit = aqui.
Sin embargo, debemos usar tipos
estáticos siempre que sea posible.
Recuerde que todos los objetos de Objective-C están en el heap y
mantendremos punteros a ellos.
No sería correcto decir “NSString digit” (es decir, sin *).
Importante!
70. Ya que todos los botones envían la misma acción a nuestro Controlador
tenemos que ver el argumento del mensaje de la acción (sender) para
averiguar cual fue tocado. Importante!
UIButton responde al mensaje currentTitle retornando un NSString
que contiene en título del botón. Vamos a usar eso para averiguar que
botón se ha tocado.
Xcode intenta diligentemente ayudarle a medida que escribe.
Es inteligente pues puede ver que lo que esta pasando en su
Para enviar un mensaje a un objeto de Objective-C se utiliza código.
una sintaxis que se inicia con un corchete abierto [, a continuación, Tenga en cuenta que desde que se declara sender como tipo
un puntero al objeto que deseamos enviar el mensaje (sender), estático Xcode solo le sugiere métodos de UIButton.
seguido de un espacio y el nombre del mensaje a enviar
(currentTitle).
Comience a escribir ahora...
71. En el mensaje enviado la sintaxis termina con un ]
para que coincida con el [ con el que se inició
Nota: este método no tiene argumentos.
Vamos a ver un método con argumentos más adelante.
Tenemos un problema.
Este pequeño triángulo es una advertencia de
que hay un problema con esta línea de código.
Un punto rojo aquí significaría un error en el
código (no se podrá compilar).
72. El triángulo de
advertencia aparece
aquí.
Otro lugar donde el triángulo
de advertencia aparece.
Haga click en el triángulo para averiguar que advertencia es
Esta advertencia parece ser correcta.
No hemos usado (todavía) la variable local digit en este método.
Nunca se debe enviar código en este curso que
tenga advertencias o errores
73. Una técnica muy simple para debuguear es registrar información en la consola.
Esto es muy fácil de hacer en Xcode.
Hay una función como printf cuya salida va a la consola llamada NSLog().
El primer argumento de NSLog() es un string, como en printf.
Tenga en cuenta que se trata de un NSString, no una const char *, así que necesitamos El resto de argumentos (como los dígitos) para NSLog()
@”” (una “constante” NSString), no simplemente “”. son una lista separada por comas de los argumentos que
Vamos a cubrir las constantes NSString de este tipo más adelante en este documento. coinciden con el % al igual que en el printf
Añadir un NSLog() para imprimir el
dígito, elegido por el usuario, en la
Note que la advertencia se ha ido
ahora que estamos utilizando digit.
Este string del mismo formato que el printf solo tiene un %.
Y es muy especial para Objective-C.
%@ significa que el argumento correspondiente a imprimir es un objeto.
En concreto significa “enviar la descripción de mensaje al objeto y utilizar el resultado como string a imprimir”.
Nuestro argumento en este caso es digit, un NSString.
NSString retorna de si mismo del método description (descripción).
Todos los objetos de iOS responden al mensaje description porque NSObject, la raíz de todas las clases, lo implementa.
74. Corremos la aplicación con NSLog() ya
puesto en su lugar
Note que al iniciar la consola esta
oculta
75. Haga click aquí para ver la Debug Area (área de depuración)
Area de depuración
76. Vamos a cubrir la depuración después.
Control de depurador: Pausa Haga click aquí para ver las variable y la consola al mismo tiempo
Control de depurador: Paso a paso
Limpia la salida de la consola (Clear)
Control de depurador: Paso de
entrada/salida
Variables Consola
77. Haga click aquí para ver solo la consola
Desplácese hasta la parte inferior de la consola
80. Puede revisar la salida de una
Detenga el simulador consola antigua en el Navegador
Vamos a borrar NSLog() y continuar implementando digitPressed:
Selecciona esta linea de código y bórrala
Note que cuando detiene la ejecución el área
de depuración desaparece automáticamente.
81. Ahora que tenemos el dígito del botón tenemos que actualizar nuestra pantalla
añadiendo el dígito en el extremo de la misma.
En realidad esto solo es una linea de código, pero vamos a dividirlo en pasos...
Vamos a hacer otra variable local llamada
myDisplay (del tipo “puntero a un UILabel”) en el
que pondremos el valor de nuestro outlet display
(que es, en sí, un “puntero a un UILabel”)
Xcode sabe que nosotros (self) solo tenemos un
método que inicia con “disp” (nuestro getter de la
@property display)
Como verás, realmente no necesitamos una variable local aquí, pero la estamos
usando solo para dejar muy claro como obtener un puntero a nuestro display.
82. Importante!
Una @property es nada más un método setter y un método getter.
Aquí estamos llamando al método getter usando la notación normal.
El método setter será llamado por el sistema en tiempo de ejecución para
conectar este outlet.
Los métodos setter y getter se crearon para nosotros por
@synthesize (el @synthesize crea también algunos de
almacenamiento para este puntero, hablaremos de esto mas tarde).
83. Las @propertys son tan importantes que hay una sintaxis especial en Objective-C
solo para setters y getters de la @property.
Se llama “dot notation” (notación punto).
Exprese la llamada al getter de nuestra
@property display usando notación punto
La versión antigua se muestra en este
comentario al final de la linea.
Se trata de dos expresiones sintácticamente diferentes pero hacen exactamente lo mismo
(es decir, llamar al getter de la @property display)
84. Ahora que tenemos un puntero a nuestro UILabel display, vamos a enviar un
mensaje para saber que texto tiene actualmente.
El mensaje a enviar se llama (muy apropiadamente) text.
Agregue esta linea de código para obtener el texto de
nuestro UILabel display y guardarlo en una variable local
(un puntero a un objeto NSString) llamada
currentDisplayText.
85. Ahora que sabemos que text es en realidad una
@property vamos a usar la notación punto
86. En realidad no hay necesidad de la variable local myDisplay.
Así que vamos a seleccionar y copiar su valor (self.display)...
91. El siguiente paso es agregar el dígito que el usuario acaba de ingresar
en el extremo de lo que se encuentra actualmente en pantalla
stringByAppendingString: es un método de la clase NSString.
Devuelve un nuevo NSString que es una copia del NSString recibido
(currentDisplay) con el argumento (digit) concatenado al final
92. Y finalmente vamos a utilizar el setter de la @property text del UILabel (setText:) para
cambiar el texto del UILabel display por el nuevo string con el dígito añadido al final.
Y si, deberíamos estar usando la notación punto aquí, pero vamos (brevemente) a usar la
notación de método para tener claro lo que estamos haciendo.
93. La tecla TAB se puede utilizar para saltar al siguiente argumento de un método.
Así luce un argumento tras haber tabulado.
Ahora solo toca escribir el nuevo argumento deseado (newDisplayText en este
caso) que lo va a remplazar (NSString *).
94. Una vez más, probablemente nunca use esta sintaxis
para establecer una @property como esta.
Es solo para efectos ilustrativos.
95. Cambia a notación punto para definir la @property text del UILabel
La notación punto para setters es exactamente la misma que la notación punto de getters, la
diferencia es que aparecen del lado izquierdo del signo igual en lugar del lado derecho.
96. En realidad no necesitamos la variable local
newDisplay, así que vamos a copiar su valor...
104. Presione 5
Hmm, esto no
debería llevar el 0,
¿ya lo notó?
105. Presione 2
Bueno al menos se
concatenan los
digitos!
106. Presione 1
Vamos a solucionar el problema del 0 a la izquierda
107. Detenga su aplicación que se ejecuta en el simulador
El problema con el cero en la izquierda es que estamos añadiendo nuevos dígitos, incluso si el usuario no
se encuentra actualmente a mitad del ingreso de un número.
El display debe ser borrado cuando el usuario comienza a escribir un nuevo número en lugar de añadir a
lo que ya esta allí (como al 0 al principio o al resultado de alguna operación más adelante)
Importante!
Para solucionar esto vamos a necesitar una @property para realizar un
seguimiento de si el usuario esta a la mitad del ingreso de un número
108. Pero no queremos agregar la @property a nuestro archivo header (.h), porque esas
propiedades son públicas.
Entonces ¿Donde agregamos propiedades privadas?
Tenemos que añadir un @interface privada a nuestro archivo de implementación (.m)
Añada una @interface privada en
su archivo de implementación
Note los ()
Importante!
A esto se llama extensión de clase.
El concepto de “público vs privado” en Objective-C se realiza a través de “archivo header (.h)
vs archivo de implementación”.
Usted declara algo público en el archivo de cabecera (header) en el bloque @interface-@end.
Usted declara algo privado en el archivo de implementación en el bloque @interface-@end.
109. Añada una propiedad boolean para
controlar si el usuario está a la mitad
del ingreso de un número
BOOL es lo que se utiliza en Objective-C para
declarar un valor booleano.
Su valor es YES o NO.
Vamos a ver NO es cero, YES es algo diferente de cero.
esta advertencia
en la siguiente
diapositiva
No se utiliza strong o weak aquí
porque un BOOL no es un puntero
Lo medios no atómicos (nonatomic) del setter y getter de esta propiedad no son seguros para los subprocesos.
Siempre se utiliza esta palabra clave, a menos que realmente sepas lo que estas haciendo.
Siempre se va a usar en este curso.
No es realmente un problema, porque a pesar de que vamos a hacer programación multi-thread en iOS,
prácticamente todos los métodos en UIKit se deben realizar en el hilo principal de ejecución en la aplicación (que
no es la interfaz de usuario la actividad que vamos a poner en otros threads).
110. Vamos a ver porque hay una
advertencia aquí
En realidad, hay 2 advertencias en
esta linea de código.
Haga click en el 2 para ver ambas.
111. El problema es que hemos declarado una @property, pero no hemos
implementado el getter (primera advertencia) y el setter (segunda advertencia).
112. Vamos a usar @synthesize (de nuevo) para
implementar el getter y el setter para nosotros!
No más advertencias
A @synthesize no le
importa si su propiedad
es pública (declarada
en .h) o privada
(declarada en .m)
Importante!
Casi siempre usaremos @synthesize para implementar getters y setters de una @property.
Pero incluso si lo hacemos, siempre se puede implementar el getters y/o el setters por nosotros.
Nuestra implementación siempre sobreescribirá a @synthesize.
@synthesize también crea una variable de instancia para almacenar nuestra @property (lo cual es bueno)
113. Se podría pensar que userIsInTheMiddleOfEnteringANumber (el
usuario esta a la mitad del ingreso de un número) es una especie
de nombre tonto para una variable. Pero el nombre largo de la
variable se recomienda en el desarrollo de iOS, ya que Xcode
completa por usted después de solo unos pocos caracteres y la
auto-documentación es muy importante para un buen estilo de
codificación.
Note que se utiliza la
notación punto para
llamar al getter de
nuestra nueva Ahora solo nos queda hacer que se
@property concatene si (if) el usuario está a la mitad del
ingreso de un número
Importante!
¿Que valor tiene userIsInTheMiddleOfEnteringANumber al iniciar?
Buena pregunta. Todas las propiedades comienzan con valor de cero.
Para un puntero a un objeto (como el display) a cero se le llama nil.
Su programa no hará crash si se envía un mensaje a nil.
Simplemente no hace nada en este caso (cualquier valor que el método retorne será igual a cero).
Para un BOOL igual a userIsInTheMiddleOfEnteringANumber, cero es NO.
114. Pero si el usuario no esta a la mitad del
ingreso de un número, simplemente iniciar
un nuevo número con el dígito que fue
ingresado
Y por supuesto, en este caso, ahora si
estamos a la mitad del ingreso de un número
115. Bien, ahora ya debería hacerlo.
Vamos a ejecutarlo otra vez
118. Presiona 1
Bueno, todos trabajan ahora!
Ahora necesitamos agregar algunos botones más (para las operaciones y Enter)
119. Presiona stop
Traer de vuelta el área
de Utilidades
120. Arrastra un botón (Round Rect
Button) de las biblioteca de
objetos hasta tu Vista
NO copies y pegues un botón de algún dígito para nuestra operación.
Copiar y pegar un botón trae la acción del botón junto a el y queremos que
los botones de operaciones envien un mensaje dferente al de los dígitos.
122. Ctrl-arrastre para crear la acción de este botón
Vamos a llamar a esta
acción operationPressed
Click en Connect
123. Ahora use copiar/pegar para crear 4 botones para las operaciones.
De nuevo, no copie/pegue un botón de dígito para hacer un botón de operación!
También debe ser un tipo estático UIButton *
124. Y cambie el título para
cada una de las 4
operaciones
125. Necesitamos un botón de Enter porque una calculadora RPN pone todos sus
operandos en un stack (pila) y trabaja en el. Enter es usado para ingresar un número
el la pila de operandos (por ejemplo, 6 Enter 5 Enter + daría como resultado 11).
Arrastra un botón (Round Rect
Button) de la biblioteca de
objetos hasta tu Vista
NO copies y pegues un botón de dígito o uno de operacion para hacer el
botón de Enter (arrastra uno nuevo). El botón de Enter tendrá una acción
diferente a la de los botones de los dígitos y de las operaciones.
126. Cambie su tamaño y su título por “Enter”
Ctrl-arrastre para crear la acción del botón Enter
Por favor ponga esta
acción antes de
operationPressed: en
Vamos a llamar a la acción de Enter desde
el archivo operationPressed:, por lo que debe ser
declarado anteriormente en el archivo
127. Vamos a llamar a esta acción enterPressed
Importante!
Pero hay algo un poco diferente en este método de la acción.
No necesitamos el argumento sender porque solo hay una tecla Enter.
Podemos controlar el mensaje de acción con solo saber que se presionó.
Cambiar Argument a None y luego hacer
click en Connect
129. Ocultamos el área de Utilidades de nuevo
Importante!
No podemos seguir adelante con la implementación sin el Modelo del MVC.
Así que vamos a tomar un tiempo de la implementación del Controlador del MVC para
ir a implementar nuestro Modelo del MVC.
130. Antes de cambiar a nuestra
redacción del Modelo, sería bueno
capturar la configuración de la pantalla
(es decir, Vista y Controlador), de modo
que podamos volver facilmente a ella
más tarde. Para eso podemos usar
pestañas en Xcode (como pestañas en
131. La nueva pestaña inicia igual que la antigua pestaña.
Una vez que iniciemos a hacer cambios podemos volver a la antigua haciendo click aquí
132. Es hora de crear nuestro Modelo.
Para ello, seleccione New File... en el menú File (Archivo)
El menú File - New File... Es la puerta a la creación de una gran variedad de cosas en una
aplicación. Incluye no solamente la nuevas clases (como en este caso), sino también
elementos de la interfaz de usuario, el esquema de la base de datos y mucho más.
133. Queremos crear una nueva clase de
Objective-C, entonces haz click aquí
Ahora haz click en Next
134. Vamos a llamar a la clase de nuestro Modelo
“CalculatorBrain” (cerebro de la calculadora), así que escríbelo acá
Nuestro Modelo va a ser una
subclase directa de NSObject
NSObject es la superclase de todos los objetos en iOS.
Las clases heredan buena parte de la funcionalidad genérica de NSObject
incluyendo el método mencionado anteriormente description, el cual devuelve
una representación NSString del objeto que es útil para la depuración con
NSLog().
Haga click en Next para elegir donde
guardar su modelo (los archivos .m y .h)
135. Asegurese de hacer click aquí para guardar los Este selector define donde
archivos del Modelo (.m y .h) en el mismo lugar aparecerán los archivos (.m y .h) de
donde están todos los demás archivos .m y .h su modelo en el sistema de archivos.
Esto define donde van a aparecer los archivos del modelo en
el Navegador (el área a la izquierda que ocultamos al inicio).
Otros archivos .m y .h en su proyecto.
No ponga los archivos de
su Modelo (.m y .h) en el
grupo del nivel superior en
el Navegador.
Pongalos en este grupo de
un nivel mas bajo
136. Cuando ya tiene las ubicaciones anteriores seleccionadas correctamente, haga click en crear (Create)
137. Cierra el Navegador para hacer más espacio
Esperemos que los archivos
de su Modelo
(CalculatorBrain.m y .h) estén
aquí con el resto de su
código fuente.
Si no, puede arrastrarlos a
donde usted quiera.
Por supuesto, usted
puede hacer más grupos
aquí haciendo click derecho
en cualquier parte del Xcode ha creado ambos archivos cabecera (header)
navegador para que e implementación para nuestro Modelo del MVC.
aparezca un menú
contextual.
138. Siempre podemos volver a nuestra Vista + Note que Xcode ha cambiado el nombre de esta pestaña automáticamente.
Controlador a través de esta pestaña Puede renombrarla haciendo doble click en ella si lo desea.
Vamos a iniciar por definir la API pública de nuestro Modelo.
Toda nuestra API pública esta en el archivo de encabezado (.h, que es lo que hace que sea público).
En las API’s públicas los métodos y las propiedades de otros objetos (además de nuestro propio
modelo) pueden invocar.
139. Primero, añadiremos este método que
proporciona una manera de ingresar operandos a
la pila de operandos de la calculadora
El argumento de este
La adición de esta API en nuestro método es un double
(número de punto
archivo de cabecera (.h) ha creado flotante de doble
una advertencia en nuestra precisión)
Este método no
retorna nada
El - significa que este es un método de instancia (es
decir, instancias de esta clase responden a ella).
También hay métodos de clase (es decir, la clase en si
responde a ella misma).
Hablaremos de eso más adelante en el curso.
140. Veamos esta advertencia
“Incomplete implementation” (implementación incompleta).
Tiene sentido.
Aun no hemos implementado pushOperand:
141. Ahora añade el método que realiza una operación
dada utilizando los operandos en la pila.
Vamos a utilizar una cadena para describir la
operación (la misma cadena que está en los botones
de funcionamiento en nuestra interfaz de usuario!).
Es un diseño bastante malo tener strings de la El argumento de este
interfaz de usuario también en su modelo, pero es método es un puntero
sencillo y vamos a ir con él para esta demostración. a un objeto (un
NSString).
Este método devuelve un
double (el resultado de realizar
la operación).
143. Vamos a tener un valor de retorno predeterminado en cero por ahora.
Vamos a poner el código real de esto en un momento.
144. ¿Cómo vamos a guardar nuestra pila de operandos?
Vamos a utilizar un arreglo (array).
"Pushing" (ingresar) se agrega un elemento a nuestra pila al final del arreglo. Importante!
"Popping" (extraer) se agarra el último elemento del arreglo, a continuación,
quitar ese elemento del arreglo.
Importante!
Necesitamos una @interface privada de nuevo
para que podamos declarar el arreglo que
necesitamos para almacenar la pila de operandos.
145. Añade la @property para nuestra pila de
operandos (operandStack)
Vamos a cubrir mucho más tarde los arreglos,
strong significa que voy mantener este objeto (el strings, etc, pero observe que el nombre de esta
arreglo) hasta que haya terminado de usarlo. clase de arreglo es NSMutableArray. La clase base
La mayoría de las @propertys no-outlet son strong. de arreglos, NSArray, no es modificable.
Está claro que no iba a funcionar para la
implementación de esta clase.
Como vimos anteriormente, la alternativa a strong (fuerte) es weak (debil).
weak significa "si nadie más está interesado en este objeto, yo tampoco, establezcamos esta
@property a nil (cero) dependiendo de cual sea el caso."
Esta vez, en nuestra implementación nuestro modelo es el único interesado en operanStack, así
que debemos hacerlo strong.
146. Veamos la advertencia
Como vimos la última vez a media que agregamos una
@property, el compilador nos advierte que tenemos que
crear su getter (operandStack) y setter (setOperandStack:).
Incluso se sugiere que se utilice @synthesize.
147. Añade @synthesize para operandStack
Y como un ejercicio vamos a escribir
exactamente lo que @synthesize generaría
Se puede ver que es bastante simple.
Solo almacena y recupera de una
variable de instancia que crea.
El hecho de que @synthesize crea una variable de
instancia con el mismo nombre que la propiedad es
peligroso, más solo es un momento.
148. Ahora que tenemos una pila, vamos a tratar
de ingresar un operando sobre ella
Uh oh.
149. Haz click aquí para ver el error
NSMutableArray es una array de objetos y un double
es un tipo primitivo, no un objeto.
150. Afortunadamente, hay una clase llamada NSNumber que
puedo utilizar para guardar los tipos primitivos en un objeto.
Ingrese el operand en un NSNumber
Este es un método de la clase de NSNumber.
No se preocupe por la sintaxis de este por ahora.
Se ve bien!
Pero hay un problema con esta línea de código.
151. Así es como se crea un objeto.
Vamos a hablar de alloc y la inicialización más tarde.
Note la prueba implícita de un puntero para ver si es nil.
También se podría decir if (operandStack == nil). Importante!
Cualquiera está bien.
Hay un lugar perfecto para inicializar operandStack.
Su getter!
Si alguien trata de obtener operandStack y no se ha
inicializado, se inicializa antes de devolverlo.
Este tipo de inicialización se llama "lazy
instantiation” (instanciación perezosa) y es un
paradigma común en iOS.
Recordemos que todas las @propertys empiezan en nil (cero).
Y recordarmos que el envío de un mensaje a nil no hace nada.
Así que esta línea de código no va a hacer nada.
En algún lugar tenemos que inicializar la @property operandStack.
153. Esto sería malo porque estaríamos accediendo a la variable de instancia
sintetizada directamente y por lo tanto no llamaríamos al getter.
Como resultado de ello, no sería gettins lazy instantiation!
Y sin embargo, no hay ninguna advertencia del compilador para ayudarnos
a darnos cuenta de eso.
Puede eliminarlo y ver que ningún error aparecerá!
154. Podemos evitar este accidente potencial en el
@synthesize al utilizar un nombre diferente para su
variable de instancia que el nombre de la propiedad.
Prefijar el nombre de la propiedad con un guión bajo es la
convención más común de nomenclatura para una variable de
instancia creada por @synthesize.
Cambiar el nombre utilizado por @synthesize para crear su variable de instancia hará que sea muy
claro cuando accidentalmente olvidemos el self.
Tenga en cuenta que hay errores ahora cuando se accede a la variable de instancia directamente.
155. SOLO los setters y getters deben acceder a la variable de instancia directamente!
Hay raras excepciones, pero por ahora, no apegaremos a esta regla.
Arregla el setter y getter para acceder a la variable de
instancia por su nuevo nombre, _operandStack
A continuación, corregir este error al poner self. antes
156. Nosotros no vamos a hacer nada con el setter, por
lo que se puede borrar.
Recuerde que @synthesize siempre va a crear lo
que no implementamos del setter y/o getter.
La única vez que no se podía aplicar esto a los setters
y a los getters es si la @property no es nonatomic (no
va a suceder en este curso).
Porque, en ese caso, usted tendría que coincidir el
@synthesize con el código de bloqueo.
157. Ahora vamos a tratar de implementar la operación +.(En performOperation)
Vamos a ver este error en un momento.
@"+" es una constante NSString.
El compilador crea un objeto NSString para usted.
Observe la @!
Sin la @, “" significa const char *. Importante!
Usted casi nunca quieren una const char * en IOS.
Usted desea los objetos NSString.
Olvidar la @ es un error común de codificación.
158. El problema es que tenemos que implementar popOperand.
Tenga en cuenta que si una advertencia o un error es
demasiado detallado para caber en la línea, puedes
pasar el ratón sobre ella para obtener una descripción
con el texto completo de la advertencia o error.
159. Implementa popOperand que retorna el último objeto (lastObject) en nuestro
arreglo operandStack, a continuación devolver el doubleValue
lastObject es un método que se hereda de
NSMutableArray NSArray que devuelve el
último objeto en el arreglo.
Todos los objetos de nuestro arreglo operandStack
son NSNumbers y NSNumber responde al método
doubleValue (que devuelve un double).
Pero eso no esta del todo bien...
160. Importante!
lastObject a un array que está vacío sólo devuelve un valor
nil (no se ha planteado una exception o hacer algo malo).
Y cualquier mensaje enviado con nil retorna nil.
Tenemos el valor del final del arreglo, pero
también tenemos que "hacer pop"
mediante la eliminación del mismo
A diferencia de lastObject, el envío a removeLastObject
con un array vacío producirá una excepción (índice fuera
de límites) y bloquearan (crash) el programa!
Es por eso que comprobamos para ver si en realidad nos
dieron una operandObject no nulo del arreglo antes de
tratar de llamar a removeLastObject ..
161. Tenga en cuenta que cada vez que enviamos
isEqualToString a la constante NSString el compilador
nos lo crea cuando usamos la notación @”*”.
Ese NSString es exactamente igual que el NSString de
operación.
Implemente la operación *
162. Debemos estar seguros de obtener en el orden correcto los operandos!
Si la entrada es “6 Enter 2 -” debe ser 4, no -4
Implemente la operación -
163. Retornamos cero en la división por cero en lugar de “not a number” (no es un número).
Somos una especie de calculadora “retorna cero en caso de fallo”.
Una vez más, estamos recibiendo el orden de los operandos correctamente
Implemente la operación /
164. Por último, debemos estar seguros de ingresar el
resultado de nuevo en la pila de manera que la siguiente
operación que se le pide que haga puede usar el
resulado.
165. Este es todo por nuestro Modelo!
Ahora vamos a volver a nuestro Controlador para terminar con el...
166. Haga clic en el nombre del archivo en la barra y
seleccione la implementación del controlador
(CalculatorViewController.m)
167. La implementación de su Controlador (.m)
debe aparecer a la izquierda.
Note que el asistente automático ha cambiado la parte derecha por el archivo de cabecera (header “.h”) de nuestro Controlador
(en lugar de nuestro Modelo).
Pero, en realidad, queremos que el archivo de cabecera de nuestro Modelo este a la derecha, porque vamos a utilizarlo en
nuestro Controlador.
Pero para tener nuestro archivo de cabecera del Modelo en el lado derecho de modo automático, tenemos que crear en nuestro
código la relación entre la salida del Controlador y el Modelo.
Lo hacemos por #importación de nuestro Modelo en la implementación de nuestro Controlador...
168. Haz un #import de nuestro archivo de cabecera
del Modelo en la implementación del Controlador
170. Haga click aquí y en “Includes” seleccionamos CalculatorBrain.h para que
aparezca en la parte derecha.
Si no ha importado (#import) a calculatorBrain.h en
su CalculatorViewController.m o no la ha guardado,
calculatorBrain.h no pueden aparecer aquí.
171. El archivo de cabecera (.h) del Modelo
debe aparecer a la derecha
Es común colocar el archivo de cabecera (API pública) de otra clase en la parte derecha de la
pantalla mientras trabajamos en la implementación de la parte izquierda
172. Bueno, estamos en la recta final.
Todo lo que necesitamos hacer es añadir un puntero (@property) en nuestro Controlador que apunte a nuestro Modelo.
Y después utilizar una instancia de nuestro Modelo para implementar operationPressed: y enterPressed.
Añade una @property para tener un
puntero en nuestro Controlador que
apunte a nuestro Modelo
174. Añadir un @synthesize para crear el getter y el
setter del brain (cerebro).
Vamos a utilizar la convención de nombres más
comunes para la variable de instancia
correspondiente (guión bajo más nombre de la
propiedad)
175. Sí, debería haber ido hacia atrás y añadir _display = y
_userIsInTheMiddleOfEnteringANumber = a estos
otros @synthesizes
Mientras estamos en ello, vamos a crear una
instancia lazily del cerebro en su método getter.
176. Ahora vamos a manejar el botón Enter cuando es tocado.
Todo lo que necesitamos hacer es ingresar el valor del
display en nuestro modelo (self.brain).
Sí, NSString responde a doubleValue también.
Estamos utilizando nuestro Se trata de analizar una salida como double de lo que esta en el string.
Modelo aquí. Por suerte, no hay manera de poner cualquier cosa, solo un número en
la pantalla de nuestra Calculadora!
Tenga en cuenta el anidamiento de las llamadas a métodos.
177. Por supuesto, si presiona Enter significa que ya
no esta a la mitad del ingreso de un número
178. Para implementar operationPressed: tenemos que ver cual
botón nos envió la acción para determinar qué operación realizar.
Si el sender de tipo estático, se puede utilizar la notación punto aquí para obtener currentTitle, por ejemplo, sender.currentTitle.
179. Ahora sólo nos queda realizar la
operación utilizando nuestro modelo.
180. Ahora actualizamos el display con el resultado
stringWithFormat: toma un string con el mismo formato que un printf.
%g significa “número de punto flotante”.
stringWithFormat: es un método de clase (no una instancia).
No te preocupes por eso por ahora.
181. Por cierto, cuando una operación se presiona y el
usuario se encuentra a la mitad del ingreso de un
número, tenemos que hacer un Enter implícito
Por ejemplo, 6 Enter 4 - haría lo
mismo que 6 Enter 4 Enter -.