Sembra che si desidera comunicare con una classe esistente che è stato progettato per prendere un delegare oggetto. Esistono numerosi approcci, tra cui:
- utilizzando una categoria per aggiungere varianti basate su blocchi dei metodi appropriati;
- utilizzare una classe derivata per aggiungere le varianti basate su blocchi; e
- 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
.
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
@zeiteisen - Buona domanda. Probabilmente conoscerai la risposta, scusami, non ho mai visto il commento. Ma per i futuri visitatori ho aggiunto un addendum. – CRD