¿Cómo automatizar mensajes con Cocoa?
Bloques en Cocoa
¿Cómo representar conjuntos de acciones que tienen que ser llevadas a cabo en serie?
Es relativamente común tener dos mensajes que se tienen que enviar en serie a un objeto, y para eso es clave automatizar mensajes con Cocoa. Especialmente cuando se manejan recursos limitados, como manejadores de ficheros, memoria, conexiones a la red, “sockets” y demás.
Por ejemplo, cuando se abre un fichero, luego hay que cerrarlo. O cuando en CoreGraphics haces un UIGraphicsPushContext(context) más te vale recordar que luego has de hacer un UIGraphicsPopContext().
La forma más común e ingenua de implementar esto es simplemente documentar esta necesidad y esperar que los usuarios del código lo recuerden. Sin embargo, esto aumenta la dificultad para entender el código así como las posibilidades de error. Además, se trata de errores serios y difíciles de localizar a posteriori, como el filtrado de recursos.
Lo ideal sería delegar este trabajo repetitivo al propio lenguaje de programación. Ciertos lenguajes, como Common Lisp resuelven este tipo de problemas con macros, como la macro with-open_file. Dicha macro se encarga de abrir el fichero, ejecutar el código que recibe como parámetro y luego cerrar el fichero.
Bloques al rescate
Los bloques no sólo se pueden usar para la multitarea con GCD, sino que tienen un enorme potencial para simplificar la sintáxis de nuestro código.
¿Podríamos hacer algo similar en Objective C? Si, usando bloques en Cocoa.
Crea un método que acepta un bloque como parámetro. Llama dicho método añadiéndole “While” al nombre del primer método que tiene que ser llamado. Luego, en el cuerpo del nuevo método, envía el primer mensaje, ejecuta el bloque y finalmente, envía el último mensaje.
Ejemplo usando bloques en Cocoa
Por ejemplo, suponiendo que los mensajes que se tienen que enviar en serie son open y close, crearíamos un método openWhile:
MyFile.h
@interface MyFile : NSObject { } -(void) openWhile:(void (^) (void)) block; -(void) open; -(void) close; @end
MyFile.m
#import "MyFile.h" @implementation MyFile -(void) openWhile:(void (^) (void)) block{ [self open]; block(); [self close]; } -(void) open{ NSLog(@"Open"); } -(void) close{ NSLog(@"Close"); } @end
Uso en Main
MyFile *f = [[MyFile alloc] init]; [f openWhile:^{NSLog(@"After open and before close");}];