2014-11-27 6 views
8

trovo nel documento Working with Blocks di Apple che la sintassi per definire un blocco che restituisce il risultato della moltiplicazione di due valori:sintassi per definire un blocco che prende un blocco e restituisce un blocco in Objective-C

double (^multiplyTwoValues)(double, double); 

è diverso da quello che definisce un blocco che prende un altro blocco come argomento e restituisce l'ennesimo blocco:

void (^(^complexBlock)(void (^)(void)))(void); 

Perché la seconda sintassi non void (^)(void)(^complexBlock)(void (^)(void))?

+0

"Perché la seconda sintassi non è ..." perché la sintassi del blocco deve adattarsi al resto del linguaggio C che ha 45 anni e tutti si aspettano che la sintassi sia così com'è. Swift ha una sintassi molto più bella del blocco, usalo se non ti piace la sintassi di Objective-C. –

risposta

14

Questo è esattamente il modo in cui la sintassi C funziona. La sintassi del blocco si basa su quella di function pointers, che si riduce all'idea di Dennis Ritchie secondo cui "la dichiarazione di una cosa dovrebbe apparire come l'uso di quella cosa".

Se si sceglie di usare il "complesso Block" è stato definito, e poi di chiamare anche la tornata di blocco nella stessa linea, sarebbe simile a questa:

complexBlock(void (^argBlock)(void){ /*...*/ })(); 
//   ^Argument (a literal Block) to the main Block 
//           ^Invocation of the returned Block 

Inoltre, l'analisi delle dichiarazioni C segue un cosiddetto "right-left rule". Il primo passo è "trova l'identificatore". Per la tua dichiarazione, è complexBlock.

void (^(^complexBlock)(void (^)(void)))(void); 
//  |  ! | Found identifier: "complexBlock is..." 

Quindi, guarda a destra. Colpiamo una parentesi chiusa, quindi questa è la fine di una dichiarazione "unità".

void (^(^   )(void (^)(void)))(void); 
//  |   ! Right parenthesis closes a part of the declaration 

Torna all'inizio della parte corrente e leggi verso sinistra fino alla parentesi di apertura. Troviamo il segno di omissione che indica un tipo di blocco. Continua a leggere a sinistra e trova una parentesi aperta, chiudendo questa parte della dichiarazione.

void (^(^    (void (^)(void)))(void); 
//  |!   | "...a Block..." 

Successivamente, andare di nuovo a destra. Qui troviamo una parentesi aperta, che indica l'inizio di una lista di parametri. Salta l'elenco dei parametri poiché ti interessa il tipo di ritorno, ma viene analizzato come sarebbe una dichiarazione autonoma.

void (^    (void (^)(void)))(void); 
//  |    !    | "...taking something or other and returning..." 

Ora che abbiamo consumato la lista dei parametri:

void (^        )(void); 
//  |       | 

continuare lo spostamento a destra, e ci ha colpito una parentesi chiusa:

void (^        )(void); 
//  |        ! 

Così, ancora una volta, indietro fino alla inizio della parte corrente e spostati a sinistra dove troviamo il punto di accesso al blocco.

void (^        (void); 
// !        | "...a Block.." 

Ecco la parte fondamentale per le tue domande su questa dichiarazione:

spostandosi a sinistra, ancora una volta troviamo una parentesi aperta, così abbiamo tornare lo spostamento a destra. Ecco perché la lista dei parametri di return Block va alla fine della dichiarazione.

void (        (void); 
// !        | Left parenthesis closes part of declaration, 
//          **now move rightwards again** 

Avendo attraversato tutto ciò, il resto dovrebbe essere evidente.

Per inciso, la pagina che ho collegato alla regola destra-sinistra ha alcune dimostrazioni come la mia, una delle quali riguarda i puntatori di funzione. Si può anche essere divertito da http://cdecl.org, che è un'implementazione online di un programma che analizza le dichiarazioni C e può aiutare a capire le varietà woolier.

+0

È una spiegazione molto dettagliata, ho chiarito molti dubbi sui blocchi dell'obiettivo C. – Kampai

+1

Una grande risposta. La sintassi C è davvero difficile da leggere. –

1

La sintassi del blocco Obj-C è piuttosto difficile da leggere, questo può essere semplificata un po 'con l'uso di typedef.

//setup 
typedef void (^ReturnedBlock)(void); 
ReturnedBlock retBlock = ^void(void){}; 

typedef void (^ParamBlock)(void); 
ParamBlock paramBlock = ^void(void){}; 

//the thing you want to do 
ReturnedBlock (^someBlock)(ParamBlock) = ^ReturnedBlock(ParamBlock param){ 

    return retBlock; 
}; 

//perform the block 
ReturnedBlock r = someBlock(paramBlock); 
+0

Your ReturnedBlock ha reso nullo, e ParamBlock ha parametro void. –

+0

mi rendo conto che puoi personalizzare i typedef come vuoi. L'idea principale è usare typedef per rendere i blocchi più leggibili e comprensibili, quindi la sintassi conta di meno. – Nick