2011-01-28 9 views
33

Diciamo che ho bisogno di comunicare con una classe che fornisce un protocollo e chiama i metodi delegato quando un'operazione è stata completata, come così:Come semplificare la logica di callback con un blocco?

@protocol SomeObjectDelegate 

@required 
- (void)stuffDone:(id)anObject; 
- (void)stuffFailed; 

@end 

@interface SomeObject : NSObject 
{ 
} 
@end 

Ora, ho deciso che mentre io potuto fare un'altra classe implementare il metodo delegato stuffDone:, ho deciso che preferirei incapsulare il processo in un blocco che è scritto da qualche parte vicino a dove SomeObject è istanziato, chiamato, ecc. Come posso fare questo? O in altre parole, se si guarda il famoso articolo this sui blocchi (nella sezione Sostituisci callback); come posso scrivere un metodo in SomeObject che accetta uno completionHandler: di ordinamenti?

risposta

42

Sembra che si desidera comunicare con una classe esistente che è stato progettato per prendere un delegare oggetto. Esistono numerosi approcci, tra cui:

  1. utilizzando una categoria per aggiungere varianti basate su blocchi dei metodi appropriati;
  2. utilizzare una classe derivata per aggiungere le varianti basate su blocchi; e
  3. scrivere una classe che implementa il protocollo e chiama i blocchi.

Ecco un modo per fare (3). In primo luogo supponiamo tuo SomeObject è:

@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 

così abbiamo qualche modo per testare - si avrà un vero e proprio SomeObject.

Ora definire una classe che implementa il protocollo e invita i blocchi forniti:

#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 

Questa classe consente di risparmiare i blocchi si passa e li chiama in risposta alle callback di protocollo. L'implementazione è semplice:

@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 

L'unica cosa che dovete ricordare è quello di Block_copy() i blocchi durante l'inizializzazione e Block_release() in un secondo momento - questo è perché i blocchi sono pila assegnate e l'oggetto può sopravvivere la sua creazione stack frame; Block_copy() crea una copia nell'heap.

Ora è possibile tutto un metodo delegato a base di passaggio si blocca:

[SomeObject testCallback:[SomeObjectBlockDelegate 
            someObjectBlockDelegateWithOnDone:^(id anObject) { NSLog(@"Done: %@", anObject); } 
            andOnFail:^{ NSLog(@"Failed"); } 
            ] 
]; 

È possibile utilizzare questa tecnica per avvolgere i blocchi per qualsiasi protocollo.

ARC Addendum

In risposta al commento: per rendere questo ARC compatibile solo rimuovere le chiamate a Block_copy() lasciando le assegnazioni dirette:

stuffDoneCallback = done; 
stuffFailedCallback = fail; 

e rimuovere il metodo dealloc. È inoltre possibile modificare Blockcopy a copy, ad esempio stuffDoneCallback = [done copy];, e questo è ciò che si potrebbe assumere dalla lettura della documentazione ARC. Tuttavia non è come l'assegnazione è a una variabile forte che fa sì che ARC mantenga il valore assegnato - e il mantenimento di un blocco di stack lo copia nell'heap. Pertanto, il codice ARC generato genera gli stessi risultati con o senza lo copy.

+0

puoi spiegare questa parte con ARC? stuffDoneCallback = Block_copy (done); stuffFailedCallback = Block_copy (errore); Mi dice che ha bisogno di un cast di bridge. – zeiteisen

+0

@zeiteisen - Buona domanda. Probabilmente conoscerai la risposta, scusami, non ho mai visto il commento. Ma per i futuri visitatori ho aggiunto un addendum. – CRD

7

Si potrebbe fare qualcosa di simile:

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; 

Poi dentro SomeObject, si potrebbe fare:

if ([self hadError]) { 
    callback([self error]); 
} else { 
    callback(nil); 
} 
+0

Puoi spiegare perché dovremmo copiare il blocco? –

1

Il collegamento seguente spiega come le richiamate tramite i delegati potrebbero essere facilmente sostituite con i blocchi.

Gli esempi includono UITableview, UIAlertview e ModalViewController.

click me

Spero che questo aiuti.

+2

Dai un'occhiata a http://stackoverflow.com/help/how-to-answer. In particolare, questa risposta potrebbe essere migliorata seguendo questa linea guida: "Citare sempre la parte più rilevante di un link importante, nel caso in cui il sito target non sia raggiungibile o sia permanentemente offline." –

Problemi correlati