Звучит так, как будто вы хотите общаться с существующим классом, который предназначен для приема объекта делегата. Существует ряд подходов, в том числе:
- использование категории для добавления блочных вариантов соответствующих методов;
- использовать производный класс для добавления блочных вариантов; и
- написать класс, который реализует протокол и вызывает ваши блоки.
Вот один из способов сделать это (3). Сначала давайте предположим, что ваш 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
так что у нас есть способ проверить - у вас будет настоящий SomeObject.
Теперь определите класс, который реализует протокол и вызывает ваши предоставленные блоки:
#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
Этот класс сохраняет блоки, которые вы передаете, и вызывает их в ответ на обратные вызовы протокола. Реализация проста:
@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
Единственное, что вам нужно запомнить, - это Block_copy () блоков при инициализации и Block_release () их позже - это потому, что блоки распределены в стеке, и ваш объект может пережить создаваемый кадр стека; Block_copy () создает копию в куче.
Теперь вы можете передавать все методы на основе делегатов, передавая ему блоки:
[SomeObject testCallback:[SomeObjectBlockDelegate
someObjectBlockDelegateWithOnDone:^(id anObject) { NSLog(@"Done: %@", anObject); }
andOnFail:^{ NSLog(@"Failed"); }
]
];
Вы можете использовать эту технику для упаковки блоков для любого протокола.
Приложение ARC
В ответ на комментарий: для обеспечения совместимости ARC просто удалите вызовы на Block_copy()
, оставив прямые назначения:
stuffDoneCallback = done;
stuffFailedCallback = fail;
и удалите метод dealloc
. Вы также можете изменить Blockcopy
на copy
, т. Е. stuffDoneCallback = [done copy];
, и это то, что вы, возможно, считаете необходимым при чтении документации ARC. Однако это не так, как присваивание сильной переменной, которая заставляет ARC сохранять назначенное значение - и сохранение блока стека копирует его в кучу. Поэтому сгенерированный код ARC дает одинаковые результаты с copy
.
или без него.