Su Objective C, un block rappresenta una porzione di codice che possiamo salvare in una variabile, passare come parametro a un metodo o persino a un altro block.
Questi costrutti vengono utilizzati comunemente per gestire la programmazione asincrona (quando ad esempio vogliamo eseguire una certa azione al termine di un'animazione o di una chiamata remota).
Vediamo la sintassi:
^{
NSLog(@"Hello Block!");
};
Come mostrato nell’esempio precedente. un blocco inizia con il simbolo ^ e poi contiene del codice arbitrario, racchiuso tra parentesi graffe.
Ecco un altro esempio:
^{
int a = 10;
int b = 20;
int sum = a + b;
NSLog(@"%d + %d = %d", a, b, sum);
};
Come dicevamo, un block può essere salvato in una variabile, che può essere definita come mostrato di seguito:
void (^firstBlock)(void);
Con questa sintassi abbiamo dichiarato una variabile avente nome firstBlock. Il tipo della variabile è un block che accetta 0 parametri (informazione che desumiamo dal void
a destra) e non ritorna alcun valore (informazione che desumiamo dal void
a sinistra).
Poiché la variabile (block con 0 parametri e void
come tipo di ritorno) è compatibile con i blocchi che abbiamo definito in precedenza, possiamo salvare uno di quei blocchi dentro firstBlock:
void (^firstBlock)(void) = ^{
int a = 10;
int b = 20;
int sum = a + b;
NSLog(@"%d + %d = %d", a, b, sum);
};
Adesso firstBlock contiene effettivamente un block che calcola la somma di 10 e 20 e poi ne stampa il risultato. Per eseguire il block contenuto in firstBlock, scriviamo:
firstBlock();
Il risultato apparirà nella console di debug:
Parametri e tipo di ritorno
Un block può ricevere dei parametri e avere un certo tipo di ritorno diverso da void, così come accade per metodi e funzioni.
Scriviamo, ad esempio, un block che riceve due interi e ne ritorna uno come risultato:
int (^sum)(int, int) = ^ (int a, int b) {
return a + b;
};
In questo caso abbiamo dichiarato una variabile sum. Il tipo è un block che accetta 2 int
come parametri e ritorna un int
. Abbiamo poi popolato la variabile con il codice che effettua la somma dei 2 parametri e li restituisce.
Ora possiamo testare il nostro blocco:
@import Foundation;
int main(int argc, const char * argv[]) {
@autoreleasepool {
int (^sum)(int, int) = ^ (int a, int b) {
return a + b;
};
int result = sum(1, 2);
NSLog(@"%d", result);
}
return 0;
}
Il risultato mostrato nella console sarà quindi 3.
Applicare un block agli elementi di un NSArray
I block possono anche essere utilizzati per applicare una certa porzione di codice a ogni elemento di una collection.
La classe NSArray, ad esempio, fornisce un metodo per applicare un block a ogni elemento dell’array.
Immaginiamo di avere questo array:
NSArray<NSString * > * languages = @[@"Objective-C", @"Java", @"Swift"];
Supponiamo di voler stampare la lunghezza di ogni stringa contenuta nell’array. Possiamo sfruttare un block come quello seguente:
[languages enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSUInteger length = [obj length];
NSLog(@"%lu", (unsigned long)length);
}];
Il codice precedente produce il seguente output:
11
4
5
Infatti, il block che abbiamo passato all’array languages (ovvero il codice che abbiamo scritto tra le parentesi graffe) è stato applicato automaticamente dalla classe NSArray a ogni elemento.