Students Save 30%! Learn & create with unlimited courses & creative assets Students Save 30%! Save Now
Advertisement
  1. Code
  2. iOS SDK
Code

Objective-C Sucintamente: Bloques

by
Difficulty:IntermediateLength:LongLanguages:
This post is part of a series called Objective-C Succinctly.
Objective-C Succinctly: Exceptions and Errors

Spanish (Español) translation by Elías Nicolás (you can also view the original English article)

Los bloques son en realidad una extensión del lenguaje de programación C, pero son muy utilizados por los frameworks Objective-C de Apple. Son similares a las lambdas de C # en que le permiten definir un bloque de sentencias en línea y pasarlo a otras funciones como si fuera un objeto.

Figure 36 Processing data with functions vs performing arbitrary actions with blocks

Procesando datos con funciones vs. realizando acciones arbitrarias con bloques

Los bloques son increíblemente convenientes para definir métodos de devolución de llamada, ya que le permiten definir la funcionalidad deseada en el punto de invocación en lugar de en otro lugar en su programa. Además, los bloques se implementan como closures (al igual que las lambdas en C #), lo que hace posible capturar el estado local que rodea al bloque sin ningún trabajo adicional.


Creando Bloques

La sintaxis de bloque puede ser un poco inquietante en comparación con la sintaxis de Objective-C que hemos estado usando a lo largo de este libro, así que no se preocupe si le lleva un tiempo sentirse cómodo con ellos. Comenzaremos mirando un ejemplo simple:

Esto define un bloque que toma un parámetro entero, x, y devuelve ese valor multiplicado por dos. Aparte de la intercalación (^), se asemeja a una función normal: tiene una lista de parámetros entre paréntesis, un bloque de instrucciones entre llaves y un valor de retorno (opcional). En C #, esto se escribe como:

Pero, los bloques no se limitan a expresiones simples, pueden contener un número arbitrario de declaraciones, como una función. Por ejemplo, puede agregar una llamada NSLog() antes de devolver un valor:

Bloques sin parámetros

Si su bloque no toma ningún parámetro, puede omitir la lista de parámetros por completo:


Uso de bloques como devoluciones de llamada

Por sí solo, un bloque no es tan útil. Normalmente, los pasará a otro método como una función de devolución de llamada. Esta es una característica de lenguaje muy potente, ya que le permite tratar functionality como un parámetro, en lugar de limitarse a data. Puede pasar un bloque a un método como lo haría con cualquier otro valor literal:

La implementación doSomethingWithBlock: puede ejecutar el bloque de la misma manera que ejecutaría una función, lo que abre la puerta a muchos nuevos paradigmas organizacionales.

Como ejemplo más práctico, veamos el método sortUsingComparator: definido por NSMutableArray. Esto proporciona la misma funcionalidad exacta que el método sortedArrayUsingFunction: que usamos en el capítulo de Data Types, excepto que usted define el algoritmo de clasificación en un bloque en lugar de una función completa:

Ejemplo de código incluido: SortUsingBlock

De nuevo, este es un orden ascendente directo, pero poder definir el algoritmo de clasificación en el mismo lugar que la invocación de la función es más intuitivo que tener que definir una función independiente en otra parte del programa. También tenga en cuenta que puede declarar variables locales en un bloque tal como lo haría en una función.

Los marcos estándar de Objective-C utilizan este patrón de diseño para todo, desde la clasificación, la enumeración y la animación. De hecho, incluso podría reemplazar el for-loop en el último ejemplo con el método enumerateObjectsUsingBlock: de NSArray, como se muestra aquí:

El parámetro obj es el objeto actual, idx es el índice actual y *stop es una forma de salir prematuramente de la enumeración. Establecer el *stop de parada en YES le indica al método que deje de enumerar después de la iteración actual. Todo este comportamiento está especificado por el método enumerateObjectsUsingBlock:.

Si bien la animación es un poco fuera de tema para este libro, todavía vale la pena una breve explicación para ayudar a entender la utilidad de los bloques. UIView es una de las clases más utilizadas en la programación de iOS. Es un contenedor gráfico genérico que le permite animar su contenido a través del método animateWithDuration:animations: El segundo parámetro es un bloque que define el estado final de la animación, y el método determina automáticamente cómo animar las propiedades usando el primer parámetro. Esta es una forma elegante y fácil de usar para definir transiciones y otros comportamientos basados ​​en temporizadores. Discutiremos las animaciones con mucho más detalle en el próximo libro de iOS Sucintamente.


Almacenamiento y ejecución de bloques

Además de pasarlos a los métodos, los bloques también se pueden almacenar en variables para su uso posterior. Este caso de uso sirve esencialmente como una forma alternativa de definir funciones:

Primero, examinemos la sintaxis para declarar variables de bloque: int (^addIntegers)(int, int). El nombre de esta variable es simplemente addIntegers (sin el caret). Esto puede ser confuso si no ha usado bloques por mucho tiempo. Es útil pensar en el cursor como la versión del bloque del operador de desreferencia (*). Por ejemplo, un puntero llamado addIntegers se declararía como *addIntegers-del mismo modo, un bloque del mismo nombre se declara como ^addIntegers. Sin embargo, tenga en cuenta que esto es simplemente una similitud superficial.

Además del nombre de la variable, también debe declarar todos los metadatos asociados con el bloque: el número de parámetros, sus tipos y el tipo de retorno. Esto permite al compilador imponer la seguridad de tipos con variables de bloque. Tenga en cuenta que el caret no es parte del nombre de la variable, solo se requiere en la declaración.

Luego, usamos el operador de asignación estándar (=) para almacenar un bloque en la variable. Por supuesto, los parámetros del bloque ((int x, int y)) coincidir con los tipos de parámetros declarados por la variable ((int, int)). También se requiere un punto y coma después de la definición del bloque, al igual que una asignación de variable normal. Una vez que se ha llenado con un valor, la variable se puede llamar como una función: addIntegers(24, 18).

Variables de bloque sin parámetro

Si su bloque no toma ningún parámetro, debe declararlo explícitamente en la variable colocando void en la lista de parámetros


Trabajando con variables

Las variables dentro de los bloques se comportan de la misma manera que en las funciones normales. Puede crear variables locales dentro del bloque, acceder a los parámetros que se pasan al bloque y usar variables y funciones globales (por ejemplo, NSLog()). Pero, los bloques también tienen acceso a variables no locales, que son variables del ámbito léxico adjunto.

En este caso, initialValue se considera una variable no local dentro del bloque porque se define fuera del bloque (no localmente, en relación con el bloque). Por supuesto, el hecho de que las variables no locales sean de solo lectura implica que no puede asignarlas:

Tener acceso a las variables circundantes (no locales) es un gran problema cuando se usan bloques en línea como parámetros del método. Proporciona una manera conveniente de representar cualquier estado requerido dentro del bloque.

Por ejemplo, si estaba animando el color de un componente de la interfaz de usuario y el color de destino se calculó y almacenó en una variable local antes de la definición del bloque, simplemente podría usar la variable local dentro del bloque, sin necesidad de trabajo adicional. Si no tuviera acceso a variables no locales, habría pasado el valor del color como un parámetro de bloque adicional. Cuando su funcionalidad de devolución de llamada se basa en una gran parte del estado circundante, esto puede ser muy engorroso.

Los bloques son cierres

Sin embargo, los bloques no solo tienen acceso a variables no locales, sino que también garantizan que esas variables nunca cambien, sin importar cuándo o dónde se ejecute el bloque. En la mayoría de los lenguajes de programación, esto se llama un cierre.

Los cierres funcionan al hacer una copia constante de solo lectura de cualquier variable no local y almacenarlas en una tabla de referencia con las declaraciones que conforman el propio bloque. Estos valores de solo lectura se utilizan cada vez que se ejecuta el bloque, lo que significa que incluso si la variable original no local cambia, se garantiza que el valor utilizado por el bloque será el mismo que cuando se definió el bloque.

Figure 37 Storing non-local variables in a reference table

Almacenamiento de variables no locales en una tabla de referencia

Podemos ver esto en acción asignando un nuevo valor a la variable initialValue del ejemplo anterior:

No importa dónde llame a addToInitialValue(), el initialValue utilizado por el bloque siempre será 32, porque eso es lo que era cuando se creó. Para todos los intentos, es como si la variable initialValue se transformara en un valor literal dentro del bloque.

Entonces, la utilidad de los bloques es doble:

  1. Te permiten representar la funcionalidad como un objeto.   
  2. Le permiten representar información de estado junto con esa funcionalidad.

La idea detrás de la funcionalidad de encapsulación en un bloque es poder usarla más adelante en el programa. Los cierres hacen posible garantizar un comportamiento predecible cuando se ejecuta un bloqueo al congelar el estado circundante. Esto los convierte en un aspecto integral de la programación de bloques.

Variables de bloque mutables

En la mayoría de los casos, capturar el estado con cierres es intuitivamente lo que cabría esperar de un bloque. Hay, sin embargo, tiempos que requieren el comportamiento opuesto. Las variables de bloque mutables son variables no locales que se designan de lectura-escritura en lugar de las predeterminadas de solo lectura. Para hacer que una variable no local sea mutable, debe declararla con el modificador __block, que crea un enlace directo entre la variable que se usa fuera del bloque y la que se usa dentro del bloque. Esto abre la puerta al uso de bloques como iteradores, generadores y cualquier otro tipo de objeto que procesa el estado.

Figure 38 Creating a direct link with a mutable block variable

Creando un enlace directo con una variable de bloque mutable.

El siguiente ejemplo le muestra cómo hacer que una variable no local sea mutable:

Esto se ve casi exactamente igual que en el ejemplo anterior, con dos diferencias muy significativas. Primero, la variable de name no local puede asignarse desde dentro del bloque. En segundo lugar, cambiar la variable fuera del bloque actualiza el valor utilizado dentro del bloque. Incluso es posible crear varios bloques que manipulan la misma variable no local.

La única advertencia al usar el modificador __block es que no se puede usar en arreglos de longitud variable.

Definiendo métodos que aceptan bloques

Podría decirse que crear métodos que acepten bloques es más útil que almacenarlos en variables locales. Te da la oportunidad de agregar tus propios métodos enumerateObjectsUsingBlock:-style a clases personalizadas.

Considere la siguiente interfaz para la clase Person:

El código void (^)(int) es el tipo de datos para el bloque que desea aceptar. En este caso, aceptaremos un bloque sin valor de retorno y un único parámetro entero. Tenga en cuenta que, a diferencia de las variables de bloque, esto no requiere un nombre para el carácter de bloque, solo un ^ sin adornos.

Ahora tiene todas las habilidades necesarias para crear métodos que acepten bloques como parámetros. Una implementación simple para la interfaz Person que se muestra en el ejemplo anterior podría tener el siguiente aspecto:

Luego, puede pasar una actividad personalizable para que se realice en el cumpleaños de Person, como:

Debería ser evidente que el uso de bloques como parámetros es infinitamente más flexible que los tipos de datos estándar que hemos estado utilizando hasta este capítulo. Realmente puedes decirle a una instancia que haga algo, en lugar de simplemente procesar datos.


Resumen

Los bloques le permiten representar sentencias como objetos de Objective-C, lo que le permite pasar acciones arbitrarias a una función en lugar de limitarse a los datos. Esto es útil para todo, desde iterar sobre una secuencia de objetos hasta animar componentes de UI. Los bloques son una extensión versátil del lenguaje de programación C, y son una herramienta necesaria si planeas trabajar mucho con los marcos estándar de iOS. En este capítulo, aprendimos cómo crear, almacenar y ejecutar bloques, y aprendimos sobre las complejidades de los cierres y el modificador de almacenamiento __block. También discutimos algunos paradigmas de uso comunes para bloques.

Así concluye nuestro viaje a través de Objective-C. Hemos cubierto todo, desde la sintaxis básica hasta los tipos de datos principales, clases, protocolos, propiedades, métodos, administración de memoria, manejo de errores e incluso el uso avanzado de bloques. Nos enfocamos más en las características del lenguaje que en crear aplicaciones gráficas, pero esto proporcionó una base sólida para el desarrollo de aplicaciones iOS. Por ahora, espero que te sientas muy cómodo con el lenguaje Objective-C.

Recuerde que Objective-C se basa en muchos de los mismos conceptos orientados a objetos que otros lenguajes OOP. Si bien solo mencionamos algunos patrones de diseño orientados a objetos en este libro, prácticamente todos los paradigmas organizacionales disponibles en otros idiomas también son posibles en Objective-C. Esto significa que puede aprovechar fácilmente su base de conocimientos orientada a objetos existente con las herramientas presentadas en los capítulos anteriores.


iOS sucintamente

Si está listo para comenzar a crear aplicaciones funcionales para iPhone y iPad, asegúrese de revisar la segunda parte de esta serie, iOS de forma sucinta. Esta guía práctica para el desarrollo de aplicaciones aplica todas las habilidades de Objective-C adquiridas de este libro a situaciones de desarrollo del mundo real. Recorreremos todos los marcos principales de Objective-C y aprenderemos cómo realizar tareas a lo largo del camino, que incluyen: configurar interfaces de usuario, capturar información, dibujar gráficos, guardar y cargar archivos, y mucho, mucho más.

Esta lección representa un capítulo de Objective-C Sucintamente, un libro electrónico gratuito del equipo de Syncfusion.
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.