objective-c - simplificacion - simplificar en logica proposicional
¿Cómo simplificar la lógica de devolución de llamada con un bloque? (3)
Digamos que necesito comunicarme con una clase que proporciona un protocolo y llama a los métodos de delegado cuando se completa una operación, como sigue:
@protocol SomeObjectDelegate
@required
- (void)stuffDone:(id)anObject;
- (void)stuffFailed;
@end
@interface SomeObject : NSObject
{
}
@end
Ahora, he decidido que si bien podría hacer que otra clase implemente el método stuffDone:
delegate, he decidido que prefiero encapsular el proceso en un bloque que está escrito en algún lugar cercano a donde SomeObject
está instanciado, llamado, etc. ¿Cómo podría hacer esto? O en otras palabras, si nos fijamos en this famoso artículo sobre bloques (en la sección Reemplazar devoluciones de llamada); ¿cómo podría escribir un método en SomeObject que acepte un completionHandler:
de géneros?
El siguiente enlace explica cómo las llamadas devueltas usando delegados se pueden reemplazar fácilmente con bloques.
Los ejemplos incluyen UITableview, UIAlertview y ModalViewController.
Espero que esto ayude.
Parece que desea comunicarse con una clase existente que está diseñada para tomar un objeto delegado. Hay una serie de enfoques, que incluyen:
- usar una categoría para agregar variantes basadas en bloques de los métodos apropiados;
- usar una clase derivada para agregar las variantes basadas en bloques; y
- escribe una clase que implemente el protocolo y llame a tus bloques.
Aquí hay una manera de hacerlo (3). Primero asumamos que su SomeObject es:
@protocol SomeObjectDelegate
@required
- (void)stuffDone:(id)anObject;
- (void)stuffFailed;
@end
@interface SomeObject : NSObject
{
}
+ (void) testCallback:(id<SomeObjectDelegate>)delegate;
@end
@implementation SomeObject
+ (void) testCallback:(id<SomeObjectDelegate>)delegate
{
[delegate stuffDone:[NSNumber numberWithInt:42]];
[delegate stuffFailed];
}
@end
así que tenemos alguna manera de probar: tendrás un SomeObject real.
Ahora defina una clase que implemente el protocolo y llame a los bloques proporcionados:
#import "SomeObject.h"
typedef void (^StuffDoneBlock)(id anObject);
typedef void (^StuffFailedBlock)();
@interface SomeObjectBlockDelegate : NSObject<SomeObjectDelegate>
{
StuffDoneBlock stuffDoneCallback;
StuffFailedBlock stuffFailedCallback;
}
- (id) initWithOnDone:(StuffDoneBlock)done andOnFail:(StuffFailedBlock)fail;
- (void)dealloc;
+ (SomeObjectBlockDelegate *) someObjectBlockDelegateWithOnDone:(StuffDoneBlock)done andOnFail:(StuffFailedBlock)fail;
// protocol
- (void)stuffDone:(id)anObject;
- (void)stuffFailed;
@end
Esta clase guarda los bloques que pasa y los llama en respuesta a las devoluciones de llamada del protocolo. La implementación es simple:
@implementation SomeObjectBlockDelegate
- (id) initWithOnDone:(StuffDoneBlock)done andOnFail:(StuffFailedBlock)fail
{
if (self = [super init])
{
// copy blocks onto heap
stuffDoneCallback = Block_copy(done);
stuffFailedCallback = Block_copy(fail);
}
return self;
}
- (void)dealloc
{
Block_release(stuffDoneCallback);
Block_release(stuffFailedCallback);
[super dealloc];
}
+ (SomeObjectBlockDelegate *) someObjectBlockDelegateWithOnDone:(StuffDoneBlock)done andOnFail:(StuffFailedBlock)fail
{
return (SomeObjectBlockDelegate *)[[[SomeObjectBlockDelegate alloc] initWithOnDone:done andOnFail:fail] autorelease];
}
// protocol
- (void)stuffDone:(id)anObject
{
stuffDoneCallback(anObject);
}
- (void)stuffFailed
{
stuffFailedCallback();
}
@end
Lo único que debe recordar es Block_copy () los bloques al inicializar y Blockselease () más tarde; esto se debe a que los bloques se asignan en pila y su objeto puede sobrevivir a su creación de marco de pila; Block_copy () crea una copia en el montón.
Ahora puedes hacer que todos los pasos basados en delegados lo bloqueen:
[SomeObject testCallback:[SomeObjectBlockDelegate
someObjectBlockDelegateWithOnDone:^(id anObject) { NSLog(@"Done: %@", anObject); }
andOnFail:^{ NSLog(@"Failed"); }
]
];
Puede usar esta técnica para envolver bloques para cualquier protocolo.
ARC Addendum
En respuesta al comentario: para hacer que este ARC sea compatible, simplemente elimine las llamadas a Block_copy()
dejando las asignaciones directas:
stuffDoneCallback = done;
stuffFailedCallback = fail;
y elimine el método dealloc
. También puede cambiar Blockcopy
para copy
, es decir, stuffDoneCallback = [done copy];
, y esto es lo que puede suponer que es necesario al leer la documentación de ARC. Sin embargo, no es como la asignación a una variable fuerte que hace que ARC retenga el valor asignado, y al retener un bloque de pila lo copia al montón. Por lo tanto, el código ARC generado produce los mismos resultados con o sin la copy
.
Podrías hacer algo como esto:
typedef void (^AZCallback)(NSError *);
AZCallback callback = ^(NSError *error) {
if (error == nil) {
NSLog(@"succeeded!");
} else {
NSLog(@"failed: %@", error);
}
};
SomeObject *o = [[SomeObject alloc] init];
[o setCallback:callback]; // you *MUST* -copy the block
[o doStuff];
...etc;
Luego dentro de SomeObject
, podrías hacer:
if ([self hadError]) {
callback([self error]);
} else {
callback(nil);
}