• Programmazione Android
  • CORSI ONLINE
  • Web Agency

Logo

Corsi di programmazione web e mobile online
Navigation
  • Home
  • CORSI ONLINE
  • Tutorial Pratici
  • GUIDE COMPLETE
    • Corso completo di C
    • Corso videogame con Cocos2d
    • Programmazione Cocoa Touch
  • Sezioni
    • Libri e manuali
    • Tips & Tricks
    • Risorse utili
    • Strumenti di Sviluppo
    • Materiale OpenSource
    • Framework
    • Guide Teoriche
    • Guide varie
    • Grafica e Design
    • iPad
    • News
    • Video Tutorial
    • Windows Phone
  • Pubblicità
  • About
    • Chi siamo
    • Pubblicazioni
    • Collabora
    • Sostieni devAPP

L#021 – Guida completa all’uso dei BLOCK

By IgnazioC | on 24 Maggio 2012 | 1 commento
Guide Teoriche

l015-objective-c-i-blocchi Abbiamo già parlato altre volte dei Block qui su devAPP, ad esempio in questo articolo introduttivo o ancora in quest’altro, dove viene spiegato come usarli per creare delle animazioni sulle UIView.
Vorrei tornarne a parlarne ancora una volta perché lo ritengo un argomento molto importante, che se conosciuto a fondo può fornire un’utile strumento per risolvere diversi problemi di programmazione.

Prima di spiegare cosa sono i block e come usarli vorrei provare a fare un passo indietro per spiegare com’era il mondo prima dei block. Scopriremo che anche in questo caso gli ingegneri Apple sono stati in grado di prendere qualcosa di già esistente e portalo ad un altro livello di astrazione rendendo il loro utilizzo davvero molto semplice.

Tutti i linguaggi di programmazione permettono di dichiarare le funzioni (verrò smentito nei commenti da chissà quale strano linguaggio…) ma in alcuni casi le funzioni costituiscono anche un vero e proprio tipo di dato, come un intero, una stringa e così via. In questo caso si dice che per quel linguaggio le funzioni sono “first-class object” (wikipedia).
Questa possibilità potremmo affermare che è alla base della programmazione ad oggetti, perché come ho avuto modo di spiegare nel corso di C una classe in fondo non è altro che una struttura (nel senso di struct) con alcune variabili locali e alcune funzioni memorizzate all’interno della struttura. Se le funzioni non si potessero trattare come normali variabili sarebbe impensabile poterle inserire all’interno di una struttura.
Ma a cosa può servire poter trattare le funzioni come tipo di dato? A fare cose molto divertenti come ad esempio una funzione che prenda in input un elenco di valori e una seconda funzione e che restituisce l’elenco dei valori ai quali ha applicato singolarmente la seconda funzione:

fun map(list,f) {
        for each l in list
                f(l)
        return list
}

Oppure la funzione potrebbe prendere in input una lista più una funzione e restiture true solo se almeno un oggetto della lista, passato alla funzione parametro, restituisce true:

fun filter(list,f) {
        for each l in list
                if f(l) return true;
}

Quindi avremmo un applicatore e un filtratore dinamici, il cui comportamento è variabile e dipende dalla funzione usata come parametro, insomma: una versione ridotta e semplificata del delegate design pattern.

Se volessimo realizzare, ad esempio, la funzione map in C, il codice che dovremmo utilizzare sarebbe questo:

#include 
#include 
int doSomeStuff(int a) {
        return a * 42;
}
void map(int *list, int size, int (*f)(int)) {
        int i = 0;
        for (i = 0; i < size; ++i)
        {
                printf(" %d ", f(list[i]));
        }
}
int main(int argc, char **argv){
        int (*f)(int); //1
        int lista[5] = {4,6,7,0,10};
        f = doSomeStuff; //2
        map(lista,5,f);
        return 0;
}

Abbiamo la funzione da applicare (doSomeStuff) abbiamo la lista dei valori (list[5]) e come si può vedere al punto //1 ho dichiarato un puntatore ad una funzione che accetta come input un intero e che restituisce un intero.
Alla nota numero //2 il puntatore è stato assegnato alla funzione doSomeStuff, e finalmente richiamiamo la funzione map.

Se non è chiaro non preoccupatevi, uno dei motivi per cui a Cupertino hanno investito sulla creazione dei block è proprio perché l'utilizzo a basso livello dei puntatori a funzione non è proprio semplice.

I linguaggi che implementano le funzioni come firt-class object sono veramente tanti, soprattutto linguaggi più moderni come python oppure lua, spesso però in questi contesti si sente parlare di closure, (wikipedia).

Qual'è la differenza?

La differenza è molto importante soprattutto perché i blocks sono un'implementazione di closure, non di semplici puntatori a funzione.

La differenza è che quando si parla di closure la funzione puntata può accedere a tutte le variabili che erano presenti nel suo scope al momento della creazione, anche se l'oggetto che l'ha dichiarata non è più in memoria..... eh? Qui è necessario fermarsi e riflettere...

Supponiamo di avere il nostro programma scritto in python, o anche in obj-c. All'interno di una classe A dichiariamo una funzione che avrà accesso ovviamente a tutte le variabili di istanza della classe A, poi creiamo un puntatore a questa funzione e lo passiamo in giro per il programma... a questo punto anche se la classe A dovesse essere deallocata, in ogni caso la funzione potrebbe accedere alle variabili di istanza perché ne ha una sua versione "congelata". Un filo più chiaro?

Ecco quindi che se abbiamo capito questo è molto più semplice inquadrare i block nel loro reale contesto, sono una molto elegante implementazione delle clousure in obj-c.

Ma come si usano?

Per spiegare come utilizzare i block in obj-c non è necessario creare un progetto iOS, possiamo usare un semplice programma a riga di comando, quindi apriamo Xcode e dal selettore del template scegliamo Application -> command line tool.

Come si dichiara un block? Dipende ovviamente dal tipo di codice al quale dovrà puntare, ad esempio questa sintassi:

int (^f)(int);

dichiara una variabile che si chiama f e che sarà di tipo block. Il block in questione dovrà ritornare un int e prendere in input un int;

Un secondo esempio come questo:

BOOL (^f2)(int);

La variabile si chiama f2, accetta un intero come parametro e restituisce un BOOL.

Per l'inizializzazione si utilizza questa sintassi:

f = ^(int num){
return num * 2;
};

In questo caso la varibabile f punta ad un blocco di codice che accetta un intero come parametro e che ne restituisce il valore moltiplicato per due.

Va sottolineato che all'interno di questa porzione di codice possiamo accedere a tutte le variabili visibili, quindi se avessimo dichiarato una variabile fuori dal block, avremmo potuto utilizzarla anche all'interno, come in questo esempio:

int multiplier = 4;
int (^f)(int);        
f = ^(int num){
return num * multiplier;
};

Poiché, come abbiamo detto, si tratta di clousure e non di puntatori a funzioni, anche se dovessimo passare la variabile f ad un'altra classe non ci sarebbero problemi per l'accesso a multiplier, perché ciasun block porta con sé una "copia" di tutte le variabili alle quali ha accesso.

Per richiamare il block si fa semplicemente così:

NSLog(@"Risultato %d",f(20));

Come si può implementare quindi in obj-c la nostra funzione map? Il concetto è del tutto simile a quanto abbiamo visto in C, cambia solo leggermente la sintassi:

/*
 * La funzione accetta la solita lista,
 * un intero che ne specifica la dimensione
 * e una variabile di tipo block.
 * Percorre la lista applicando il block all'elemento e stampando il valore
 */
void map(int *lista, int size, int (^f)(int) ){
    for (int i = 0; i < size; i++) {
        NSLog(@"%d", f(lista[i]));
    }
}

int main(int argc, const char * argv[])
{
    @autoreleasepool {
        //dichiarazione e inizializzazione della lista.
        int lista[5] = {1,2,3,4,5};
       
        //variabile esterna visibile anche dentro il block.
        int multiplier = 4;
       
        //variabile di tipo block
        int (^f)(int);
       
        //assegnazione della variabile block
        f = ^(int num){
            return num * multiplier;
        };
       
        //invocazione della funzione map
        map(lista, 5, f);
       
    }
    return 0;
}

Se siete arrivati fin qui adesso i block non dovrebbero avere più segreti, proviamo infatti ad esaminare un metodo tra i tanti che li utilizzano, ad esempio guardiamo il prototipo di:

- (void)enumerateObjectsUsingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))block

Questo metodo cosa ci dice?

E' un metodo di istaza (il -) non restituisce alcun valore (void) e accetta un parametro di tipo block, che a sua volta non restituisce nulla e accetta tre parametri, un id, un intero e un puntatore a BOOL

Se lo volessimo usare allora cosa dovremmo fare?

  1. Dichiarare una variabile di tipo block che accetti il giusto numero e tipo di parametri, in questo modo:
    void (^p)(id, NSUInteger, BOOL *); //la variabile in questo caso si chiama p
  2. Assegnare la variabile ad una porzione di codice corretta:
    p = ^(id obj, NSUInteger index, BOOL *stop){
                NSLog(@"At index: %lu obj=%@", index, obj);
    };
    
  3. Invocare il metodo enumerateObjectsUsingBlock su un nostro array passando il block come parametro:
    NSArray *array = [NSArray arrayWithObjects:@"ONE",@"TWO", @"THREE", nil];
    [array enumerateObjectsUsingBlock:p];
    

Questo è ovviamente il modo più prolisso, ma che fa capire bene cosa sta succedendo, probabilmente vi capiterà più spesso di saltare la dichiarazione e di mettere il block direttamente nell'invocazione, in questo modo:

NSArray *array = [NSArray arrayWithObjects:@"ONE",@"TWO", @"THREE", nil];
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger index, BOOL *stop){
NSLog(@"At index: %lu obj=%@", index, obj);
}];

Una domanda per i lettori: Perché l'ultimo parametro è un puntatore a BOOL? Date la vostra risposta nei commenti!

Prima di lasciarci un'ultima informazione molto utile quando si usano i block, abbiamo detto che tutte le variabili accessibili al block al momento della sua creazione (variabili globali, di istanza etc) sono "copiate" insieme al block in modo che anche se la sua esecuzione dovesse avvenire fuori dallo scope originale non ci sarebbero problemi di visibilità... non abbiamo specificato però che queste variabili sono accessibili in sola lettura, perché gestirne anche la scrittura sarebbe un tantino oneroso per il sistema e poi non vogliamo realmente che del codice, in giro per il programma, possa modificare tutte le nostre variabili... non sarebbe molto pulito.

Esiste un modo però per far sì che una variabile sia copiata in lettura e scrittura e consiste nell'utilizzare il modificatore di scope __block prima della dichiarazione, come in questo esempio:

         __block int multiplier = 4; //la variabile adesso è modificabile anche dall'interno del block
int (^f)(int);        
        //assegnazione della variabile block
        f = ^(int num){
            multiplier = multiplier + 1;
            return num * multiplier;
        };

Con questo credo proprio di aver soddisfatto la vostra curiosità sui block, per domande e commenti visitate il nostro forum o utilizzate i commenti qui in basso.

Alla prossima!

Share this story:
  • tweet

Tags: __blockblockclosurefirst-class objectprogrammare objective-cstruct

Recent Posts

  • Parte il percorso programmatori iOS in Swift su devACADEMY.it

    20 Dicembre 2017 - 0 Comment
  • Android, crittografare dati velocemente con Encryption

    24 Settembre 2018 - 0 Comment
  • Sql2o, accesso immediato ai database tramite Java

    3 Settembre 2018 - 0 Comment
  • Okio, libreria per ottimizzare l’input/output in Java

    27 Agosto 2018 - 0 Comment

Related Posts

  • Effective Objective-C 2.0: 52 Specific Ways to Improve Your iOS and OS X Programs

    21 Ottobre 2013 - 12 Comments

Author Description

One Response to “L#021 – Guida completa all’uso dei BLOCK”

  1. 24 Maggio 2012

    simone

    il puntatore a bool permette di modificare il valore della variabile puntata

Leave a Reply

Your email address will not be published. Required fields are marked *


*
*

Corso online di programmazione android e java

SEZIONI

  • Android
  • Comunicazioni
  • Contest
  • Corsi ed Eventi
  • Corso completo di C
  • Corso programmazione videogiochi
  • Framework
  • Grafica e Design
  • Guida rapida alla programmazione Cocoa Touch
  • Guide Teoriche
  • Guide varie
  • iPad
  • Le nostre applicazioni
  • Libri e manuali
  • Materiale OpenSource
  • News
  • Pillole di C++
  • Progetti completi
  • Risorse utili
  • Strumenti di Sviluppo
  • Swift
  • Tips & Tricks
  • Tutorial Pratici
  • Video Tutorial
  • Windows Phone

Siti Amici

  • Adrirobot
  • Allmobileworld
  • Apple Notizie
  • Apple Tribù
  • Avvocato360
  • Blog informatico 360°
  • bubi devs
  • fotogriPhone
  • GiovaTech
  • iApp-Mac
  • iOS Developer Program
  • iPodMania
  • MelaRumors
  • Meritocracy
  • SoloTablet
  • TecnoUser
  • Privacy & Cookie Policy
©2009-2018 devAPP - All Rights Reserved | Contattaci
devAPP.it è un progetto di DEVAPP S.R.L. - Web & Mobile Agency di Torino
Str. Volpiano, 54 - 10040 Leini (TO) - C.F. e P.IVA 11263180017 - REA TO1199665 - Cap. Soc. € 10.000,00 i.v.

devACADEMY.it

Vuoi imparare a programmare?

Iscriviti e accedi a TUTTI i corsi con un’unica iscrizione.
Oltre 70 corsi e migliaia di videolezioni online e in italiano a tua disposizione.

ISCRIVITI SUBITO