Dichiarare un block type per un completion handerl

In relazione all’articolo Creare un completion handler, si può precisare quanto illustrato qui:

La dichiarazione del block type è un modo per rendere più facilmente modificabile la signature del metodo, poiché nel primo caso basta cambiare la definizione del blocco, senza toccare il metodo.

1
2
3
4
typedef BOOL (^SomeBlockType)(id object, NSUInteger idx, BOOL *stop);
 
- (void)collectionToCheck:(SomeBlockType)checkerBlock;
- (void)singleItemToCheck:(SomeBlockType)checkerBlock;
typedef BOOL (^SomeBlockType)(id object, NSUInteger idx, BOOL *stop);

- (void)collectionToCheck:(SomeBlockType)checkerBlock;
- (void)singleItemToCheck:(SomeBlockType)checkerBlock;

versus:

1
2
- (void)collectionToCheck:(BOOL(^)(id object, NSUInteger idx, BOOL *stop)) checkerBlock;
- (void)singleItemToCheck:(BOOL(^)(id object, NSUInteger idx, BOOL *stop)) checkerBlock;
- (void)collectionToCheck:(BOOL(^)(id object, NSUInteger idx, BOOL *stop)) checkerBlock;
- (void)singleItemToCheck:(BOOL(^)(id object, NSUInteger idx, BOOL *stop)) checkerBlock;

Creare un completion handler

Comodissimi sono i blocchi di codice eseguiti quando un certo metodo si conclude, in particolare animazioni e metodi in background, gestiti con GCD (Grand Central Dispatch) o direttamente da classi Apple, come NSURLConnection. Può essere quindi utile saper creare un completion handler, ovvero un blocco di codice che viene eseguito solo quando un metodo effettivamente si conclude, espandendo quindi le funzionalità in maniera importante.

Innanzitutto si dichiara il blocco di ritorno del completion handler, avendo cura di inserire gli eventuali oggetti e variabili da ritornare: ad esempio un codice che effettui il download di un  pacchetto di dati potrebbe restituire un oggetto NSData e/o un BOOL success:

1
typedef void(^ReturnBlock)(NSData *data, BOOL success);
typedef void(^ReturnBlock)(NSData *data, BOOL success);

Il completion handler può essere quindi una variabile di istanza di tipo ReturnBlock dichiarata nello stesso file d’interfaccia in cui si è definito il ReturnBlock:

1
2
3
4
@interface MyClass : NSObject
{
    ReturnBlock _completionHandler;
}
@interface MyClass : NSObject
{
    ReturnBlock _completionHandler;
}

Il metodo che fa uso di questo completio handler sarà qualcosa del genere

1
- (void)doSomethingWithCompletionHandler:(ReturnBlock)aCompletionHandler;
- (void)doSomethingWithCompletionHandler:(ReturnBlock)aCompletionHandler;

Nella cui implementazione, se ad esempio si sta usando la classe NSURLConnection per gestire il download, si scriverà nel metodo delegato:

1
2
3
4
5
6
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
 
if( _completionHandler )
    _completionHandler(data, YES);
 
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{

if( _completionHandler )
    _completionHandler(data, YES);

}

Quindi, ovunque allochiamo e inizializiamo una istanza di MyClass, possiamo anche usare il seguente modo il metodo:

1
2
3
[myClass doSomethingWithCompletionHandler:^(NSData *data, BOOL success)^{
 
}];
[myClass doSomethingWithCompletionHandler:^(NSData *data, BOOL success)^{

}];

il blocco di codice verrà eseguito solo al completamento di qualunque cosa doSomething faccia, passando l’oggetto data e la variabile booleana success.

N.B.: con ARC non c’è bisogno di copiare il completion handler nel metodo doSomething né di rilasciarlo in dealloc.