web-dev-qa-db-ja.com

dispatch_asyncと元のキューの完了ハンドラの呼び出し

私はいくつかの関連する質問を見てきましたが、誰もこのケースに答えるようには見えません。バックグラウンドで動作するメソッドを記述したいと思います。元のメソッド呼び出しに使用したのと同じスレッド/キューで完了コールバックを呼び出すには、このメソッドが必要です。

- (void)someMethod:(void (^)(BOOL result))completionHandler {
    dispatch_queue_t current_queue = // ???

    // some setup code here
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        BOOL ok = // some result

        // do some long running processing here

        dispatch_async(current_queue, ^{
            completionHandler(ok);
        });
    });

sameMethodの呼び出しと同じキューまたはスレッドで完了ハンドラーが呼び出されるように、ここではどの魔法の呪文が必要ですか?メインスレッドを想定したくありません。そしてもちろん dispatch_get_current_queueは使用されません。

25
rmaddy

Apple docsを見ると、2つのパターンがあるように見えます。

完了ハンドラーがメインスレッドで実行されることが想定されている場合、キューを提供する必要はありません。例は、UIViewanimationsメソッドです。

+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion

それ以外の場合、APIは通常、呼び出し元にキューを提供するように要求します。

[foo doSomethingWithCompletion:completion targetQueue:yourQueue];

私の提案は、このパターンに従うことです。完了ハンドラーを呼び出す必要があるキューが明確でない場合、呼び出し元はそれをパラメーターとして明示的に指定する必要があります。

13
D.C.

メインキューは別として、特定のスレッドで実行されることが保証されていないため、実際にはキューを使用できません。代わりに、スレッドを取得してブロックを直接実行する必要があります。

Mike Ash's Block Additions からの適応:

_// The last public superclass of Blocks is NSObject
@implementation NSObject (rmaddy_CompletionHandler)

- (void)rmaddy_callBlockWithBOOL: (NSNumber *)b
{
    BOOL ok = [b boolValue];
    void (^completionHandler)(BOOL result) = (id)self;
    completionHandler(ok);
}

@end
_

_- (void)someMethod:(void (^)(BOOL result))completionHandler {
    NSThread * origThread = [NSThread currentThread];

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        BOOL ok = // some result

        // do some long running processing here

        // Check that there was not a nil handler passed.
        if( completionHandler ){
            // This assumes ARC. If no ARC, copy and autorelease the Block.
            [completionHandler performSelector:@selector(rmaddy_callBlockWithBOOL:)
                                      onThread:origThread
                                    withObject:@(ok)    // or [NSNumber numberWithBool:ok]
                                 waitUntilDone:NO];
        }
        });
    });
_

dispatch_async()を使用していませんが、元のディスパッチされたタスクブロック内に含まれているため、これはプログラムの残りの部分に関しては非同期です。また、_waitUntilDone:NO_は、それ。

6
jscs

これで問題が解決するかどうかはわかりませんが、GCDの代わりにNSOperationsを使用するのはどうですか?:

- (void)someMethod:(void (^)(BOOL result))completionHandler {
NSOperationQueue *current_queue = [NSOperationQueue currentQueue];

// some setup code here
NSOperationQueue *q = [[NSOperationQueue alloc] init];
[q addOperationWithBlock:^{
    BOOL ok = YES;// some result

    // do some long running processing here
    [current_queue addOperationWithBlock:^{
        completionHandler(ok);
    }];
}];
3
Arian Sharifian

@rmaddyが述べたように、いくつかのキューでいくつかのタスクを実行してから、完了ブロックを実行したいと思っていました。 Apple=から同時実行プログラミングガイドに出会い、これを実装しました(disp_retain&dispatch_releasedはARCを使用しているためコメントアウトされています)- https://developer.Apple.com/ library/ios/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html#// Apple_ref/doc/uid/TP40008091-CH102-SW1

void average_async(int *data, size_t len, dispatch_queue_t queue, void (^block)(int))
{
// Retain the queue provided by the user to make
// sure it does not disappear before the completion
// block can be called.
//dispatch_retain(queue); // comment out if use ARC

// Do the work on user-provided queue
dispatch_async(queue, ^{
  int avg = average(data, len);
  dispatch_async(queue, ^{ block(avg);});

  // Release the user-provided queue when done
  //dispatch_release(queue); // comment out if use ARC
});
}
0
Loozie