Ответ 1
Похоже, вы хотите общаться с существующим классом, который предназначен для принятия объекта делегата. Существует ряд подходов, в том числе:
- с использованием категории для добавления вариантов на основе блоков соответствующих методов;
- использовать производный класс для добавления вариантов на основе блоков; и
- напишите класс, который реализует протокол и вызывает ваши блоки.
Вот один из способов сделать (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
.