Я пишу класс черного ящика, который выполняет тяжелую обработку в фоновом режиме, используя 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
основан на стеке и должен быть скопирован в кучу, чтобы использовать его в другом потоке ...
Редактировать: я обнаружил, что Mike Ash используеточень похожий подход с:
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;