私はブロックの大ファンですが、並行性のためにそれらを使用していません。少しグーグルした後、私はこのアイデアをつなぎ合わせて、学んだことをすべて1か所に隠しました。目標は、バックグラウンドでブロックを実行し、それが終了したら、別のブロック(UIViewアニメーションなど)を実行することです...
- (NSOperation *)executeBlock:(void (^)(void))block completion:(void (^)(BOOL finished))completion {
NSOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:block];
NSOperation *completionOperation = [NSBlockOperation blockOperationWithBlock:^{
completion(blockOperation.isFinished);
}];
[completionOperation addDependency:blockOperation];
[[NSOperationQueue mainQueue] addOperation:completionOperation];
NSOperationQueue *backgroundOperationQueue = [[NSOperationQueue alloc] init];
[backgroundOperationQueue addOperation:blockOperation];
return blockOperation;
}
- (void)testIt {
NSMutableString *string = [NSMutableString stringWithString:@"tea"];
NSString *otherString = @"for";
NSOperation *operation = [self executeBlock:^{
NSString *yetAnother = @"two";
[string appendFormat:@" %@ %@", otherString, yetAnother];
} completion:^(BOOL finished) {
// this logs "tea for two"
NSLog(@"%@", string);
}];
NSLog(@"keep this operation so we can cancel it: %@", operation);
}
私の質問は次のとおりです。
ありがとう。
私はNSOperationまたはNSOperationQueuesの専門家ではありませんが、まだいくつかの注意点があると思いますが、以下のコードの方が少し優れていると思います。おそらくいくつかの目的には十分ですが、並行性の一般的な解決策ではありません。
- (NSOperation *)executeBlock:(void (^)(void))block
inQueue:(NSOperationQueue *)queue
completion:(void (^)(BOOL finished))completion
{
NSOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:block];
NSOperation *completionOperation = [NSBlockOperation blockOperationWithBlock:^{
completion(blockOperation.isFinished);
}];
[completionOperation addDependency:blockOperation];
[[NSOperationQueue currentQueue] addOperation:completionOperation];
[queue addOperation:blockOperation];
return blockOperation;
}
今それを使用しましょう:
- (void)tryIt
{
// Create and configure the queue to enqueue your operations
backgroundOperationQueue = [[NSOperationQueue alloc] init];
// Prepare needed data to use in the operation
NSMutableString *string = [NSMutableString stringWithString:@"tea"];
NSString *otherString = @"for";
// Create and enqueue an operation using the previous method
NSOperation *operation = [self executeBlock:^{
NSString *yetAnother = @"two";
[string appendFormat:@" %@ %@", otherString, yetAnother];
}
inQueue:backgroundOperationQueue
completion:^(BOOL finished) {
// this logs "tea for two"
NSLog(@"%@", string);
}];
// Keep the operation for later uses
// Later uses include cancellation ...
[operation cancel];
}
あなたの質問に対するいくつかの答え:
キャンセル。通常、NSOperationをサブクラス化して、self.isCancelled
をチェックして先に戻ることができるようにします。 このスレッド を参照してください。これは良い例です。現在の例では、NSBlockOperation
を作成するために指定しているブロックから操作がキャンセルされているかどうかを確認できません。その時点では、そのような操作はまだないためです。ブロックが呼び出されている間にNSBlockOperation
sをキャンセルすることは明らかに可能ですが、 面倒 。 NSBlockOperation
sは、特定の簡単なケース用です。キャンセルが必要な場合は、NSOperation
をサブクラス化することをお勧めします:)
ここでは問題はありません。 2つのことに注意してください。 a)現在のキューで完了ブロックを実行するようにメソッドdoを変更しました。b)パラメーターとしてキューが必要です。 @Mike Wellerが言ったように、background queue
を指定する方がよいでしょう。そうすれば、操作ごとに1つ作成する必要がなく、実行に使用するキューを選択できます:)
はい、string
atomic
を作成する必要があると思います。忘れてはならないことの1つは、キューに複数の操作を指定すると、それらが(必然的に)その順序で実行されない可能性があるため、string
に非常に奇妙なメッセージが表示される可能性があることです。一度に1つの操作を連続して実行する必要がある場合は、次の操作を実行できます。[backgroundOperation setMaxConcurrentOperationCount:1];
操作のキューイングを開始する前。 docs にも、読む価値のあるメモがあります。
追加の操作キューの動作操作キューは、優先度と準備状況に基づいて、キューに入れられた操作オブジェクトを実行します。キューに入れられたすべての操作オブジェクトの優先度が同じで、キューに入れられたときに実行する準備ができている場合、つまり、isReadyメソッドがYESを返す場合、それらはキューに送信された順序で実行されます。同時操作の最大数が1に設定されているキューの場合、これはシリアルキューに相当します。ただし、操作オブジェクトのシリアル実行に依存しないでください。操作の準備が変更されると、結果の実行順序が変更される可能性があります。
私はあなたが知っているこれらの行を読んだ後だと思います:)
executeBlock:completion:
呼び出しごとに新しいNSOperationQueue
を作成しないでください。これはコストがかかり、このAPIのユーザーは、一度に実行できる操作の数を制御できません。
NSOperation
インスタンスを返す場合は、それらを追加するキューを決定するのは呼び出し元に任せる必要があります。しかし、その時点では、メソッドは実際には何の役にも立たず、呼び出し元はNSBlockOperation
を自分で作成することもできます。
バックグラウンドでブロックをスピンオフし、終了時にコードを実行する簡単で簡単な方法が必要な場合は、dispatch_*
関数を使用して簡単なGCD呼び出しを行う方がよいでしょう。例えば:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// do your background work
// ...
// now execute the 'completion' code.
// you often want to dispatch back to the main thread to update the UI
// For example:
dispatch_async(dispatch_get_main_queue(), ^{
// update UI, etc.
myLabel.text = @"Finished";
});
});
完了時に実行するブロックを設定して、このような依存関係を追加する必要はありません。 NSBlockOperation
すべてのNSOperation
サブクラスと同様に、すでに completionBlock
プロパティがあり、ブロックが作業を終了すると自動的に実行されます。
@property(copy) void (^completionBlock)(void);
完了ブロックは、そのブロックがfinished
状態に移行したときに実行されます。