Il semble que vous souhaitiez communiquer avec une classe existante qui est conçue pour prendre un objet délégué. Il existe un certain nombre d'approches, notamment :
- en utilisant une catégorie pour ajouter des variantes basées sur les blocs des méthodes appropriées ;
- utiliser une classe dérivée pour ajouter les variantes basées sur les blocs ; et
- écrire une classe qui implémente le protocole et appelle vos blocs.
Voici une façon de procéder (3). Tout d'abord, supposons que votre SomeObject est :
@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
ainsi nous avons un moyen de tester - vous aurez un vrai SomeObject.
Définissez maintenant une classe qui implémente le protocole et appelle vos blocs fournis :
#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
Cette classe enregistre les blocs que vous lui passez et les appelle en réponse aux rappels du protocole. L'implémentation est simple :
@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
La seule chose dont vous devez vous souvenir est de Block_copy() les blocs lors de l'initialisation et de Block_release() plus tard - c'est parce que les blocs sont alloués par la pile et que votre objet peut dépasser sa trame de pile de création ; Block_copy() crée une copie dans le tas.
Maintenant vous pouvez toutes les méthodes basées sur les délégués en leur passant des blocs :
[SomeObject testCallback:[SomeObjectBlockDelegate
someObjectBlockDelegateWithOnDone:^(id anObject) { NSLog(@"Done: %@", anObject); }
andOnFail:^{ NSLog(@"Failed"); }
]
];
Vous pouvez utiliser cette technique pour envelopper des blocs pour n'importe quel protocole.
Addendum à l'ARC
En réponse au commentaire : pour rendre ce système compatible avec l'ARC, il suffit de supprimer les appels à Block_copy()
quitter les affectations directes :
stuffDoneCallback = done;
stuffFailedCallback = fail;
et retirer le dealloc
méthode. Vous pouvez également modifier Blockcopy
a copy
c'est-à-dire stuffDoneCallback = [done copy];
et c'est ce que vous pouvez supposer être nécessaire en lisant la documentation de l'ARC. Cependant, ce n'est pas le cas, car l'affectation est faite à une variable forte, ce qui amène l'ARC à conserver la valeur affectée - et la conservation d'un bloc de pile la copie dans le tas. Par conséquent, le code ARC généré produit les mêmes résultats avec ou sans l'attribut copy
.