Я пишу класс черного ящика, который выполняет тяжелую обработку в фоновом режиме с помощью Grand Central Dispatch. Я намерен предоставить API стиля продолжения, что-то вроде:
- (void) processHeavyStuff:(id) someParam thenDo:(ContinuationBlock)followup;
Который клиент может вызвать, например, так:
[myBlackBox processHeavyStuff:heavyOne thenDo: ^(Dalek* result){
[self updateDisplayWithNewDalek:result];
}];
Что обычно делается, так это то, что реализация processHeavyStuff:thenDo:
вызывает свой блок продолжения в основном потоке, используя dispatch_get_main_queue()
. См. метод вызова модели с блоком, который будет запустить в основном потоке для примера.
Однако этот распространенный сценарий предполагает, что клиент звонит из основного потока. Я хотел бы быть более общим и вызвать блок продолжения в потоке вызывающей стороны, который может быть или не быть основным потоком. Это позволит, например, хорошо работать с многопоточными клиентами Core Data, где NSManagedObjectContext
является локальным для потока. Есть ли хороший шаблон для этого?
Используя –[NSObject performSelector:onThread:withObject:waitUntilDone:]
, я вижу, что могу определить вспомогательный метод:
- (void) callContinuation:(ContinuationBlockWithNoArgument) followup
{
followup();
}
А затем выполните этот селектор в потоке вызывающего абонента:
- (void) processHeavyStuff:(id) someParam thenDo:(ContinuationBlock)followup
{
NSSthread *callerThread = [NSThread currentThread];
dispatch_async(self.backgroundQueue, ^ {
Dalek *newDalek = [self actuallyDoTheHeavyProcessing:someParam];
[self performSelector:@selector(callContinuation:) onThread:callerThread
withObject: ^{
followup(newDalek);
}
waitUntilDone:NO];
});
}
Я думаю, это может сработать, и я собираюсь попробовать. Но есть ли что-то менее надуманное? Возможно версия performSelector:onThread:
для блоков?
PS: Для ясности я исключил все вызовы управления памятью из приведенных выше фрагментов. Например, блок followup
основан на стеке и должен быть скопирован в кучу, чтобы использовать его в другом потоке...
Изменить: я узнал, что Майк Эш использует очень похожий подход с:
void RunOnThread(NSThread *thread, BOOL wait, BasicBlock block)
{
[[[block copy] autorelease] performSelector: @selector(my_callBlock) onThread: thread withObject: nil waitUntilDone: wait];
}
Где my_callBlock
определяется в категории NSObject
:
@implementation NSObject (BlocksAdditions)
- (void)my_callBlock
{
void (^block)(void) = (id)self;
block();
}
@end;