Edited at
iOSDay 8

NSBlockOperationで手軽にキャンセル処理

More than 5 years have passed since last update.

GCD便利ですね。手軽にネストした非同期処理を書けるので、使いまくっています。しかし一直線にコードを書いていると見過ごしがちなのが、キャンセル処理です。時間のかかる処理を途中で止める場合や、別のUIViewControllerに遷移するから処理そのものが必要なくなった場合に、処理を止めたい場合があります。具体的には以下の様なケースがあると思います。


  • 大きなファイルのアップロード中にキャンセルボタンを出したい

  • 画面遷移をしたら、前の画面で進行中の画像ダウンロードはキャンセルしたい

このような場合にGCDライクに使えて、しかもキャンセル処理ができのが組み込みクラスのNSBlockOperationです。早速使い方を見て行きましょう。weakのおかげでややこしいメモリ管理を考えなくて楽です。

-(void)heavyTask {

if (!_queue)
_queue = [[NSOperationQueue alloc] init];

NSBlockOperation *operation = [[NSBlockOperation alloc] init];
__weak NSBlockOperation *weakOperation = operation; // 循環参照を避ける
[operation addExecutionBlock:^{
for (int i = 0; i < 10; i++) {
if (weakOperation.isCancelled) return; // これを至る所に挟む
[NSThread sleepForTimeInterval:1]; // 何か重い処理
}
NSLog(@"complete");
}];

[_queue addOperation:operation];
}

-(void)cancel {
[_queue cancelAllOperations];
}

ではネストした処理のキャンセルを考えましょう。全く同じように直感的に書けます。NSOprationQueueがキャンセルの面倒を見てくれるので楽ですね。


-(void)heavyTask {
if (!_queue)
_queue = [[NSOperationQueue alloc] init];

__weak NSOperationQueue *weakQueue = _queue;

NSBlockOperation *firstOperation = [[NSBlockOperation alloc] init];
__weak NSBlockOperation *weakFirstOperation = firstOperation; // 循環参照を避ける

[firstOperation addExecutionBlock:^{
for (int i = 0; i < 10; i++) {
if (weakFirstOperation.isCancelled) return; // これを至る所に挟む
[NSThread sleepForTimeInterval:1]; // 何か重い処理
}
NSLog(@"next");
NSBlockOperation *secondOperation = [[NSBlockOperation alloc] init];
__weak NSBlockOperation *weakSecondOperation = secondOperation; // 循環参照を避ける
[secondOperation addExecutionBlock:^{
for (int i = 0; i < 10; i++) {
if (weakSecondOperation.isCancelled) return;
[NSThread sleepForTimeInterval:1]; // 何か重い処理
}
NSLog(@"complete");
}];
[weakQueue addOperation:secondOperation];
}];

[_queue addOperation:firstOperation];
}

-(void)cancel {
[_queue cancelAllOperations];
}

ここまでやるとだいぶコードが見づらいですね…。しかしDelegate等で書くと、(設計的には綺麗ですが)後から追いづらくなるので、こっちの方がましではないかと思ってます。

以上NSBlockOperationでキャンセル処理を行う方法の紹介でした。NSOperationQueueは裏側でGCDを使っているので、GCD+キャンセル機能が欲しいという用途には最適だと思います。