27

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

- (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使用されるべきではありません。

4

4 に答える 4

13

Apple のドキュメントを見ると、2 つのパターンがあるようです。

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

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

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

[foo doSomethingWithCompletion:completion targetQueue:yourQueue];

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

于 2012-10-27T08:37:38.110 に答える
6

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

Mike Ash の 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それに対しても非同期になっているためです。

于 2012-10-27T08:14:35.563 に答える
3

これで問題が解決するかどうかはわかりませんが、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);
    }];
}];
于 2013-05-07T20:04:26.817 に答える
0

@rmaddyが言及したように、いくつかのキューでいくつかのタスクを実行してから、完了ブロックを実行したかったのです。Apple の Concurrency Programming Guide に出会い、これを実装しました (ARC を使用しているため、dispatch_retain と dispatch_released はコメントアウトされています) -- 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
});
}
于 2013-12-09T19:55:18.353 に答える