NSOperation
をサブクラス化して並行処理を行い、キャンセルをサポートする方法に関する適切なドキュメントを見つけることができません。 Appleのドキュメントを読みましたが、「公式」の例を見つけることができません。
ここに私のソースコードがあります:
@synthesize isExecuting = _isExecuting;
@synthesize isFinished = _isFinished;
@synthesize isCancelled = _isCancelled;
- (BOOL)isConcurrent
{
return YES;
}
- (void)start
{
/* WHY SHOULD I PUT THIS ?
if (![NSThread isMainThread])
{
[self performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:NO];
return;
}
*/
[self willChangeValueForKey:@"isExecuting"];
_isExecuting = YES;
[self didChangeValueForKey:@"isExecuting"];
if (_isCancelled == YES)
{
NSLog(@"** OPERATION CANCELED **");
}
else
{
NSLog(@"Operation started.");
sleep(1);
[self finish];
}
}
- (void)finish
{
NSLog(@"operationfinished.");
[self willChangeValueForKey:@"isExecuting"];
[self willChangeValueForKey:@"isFinished"];
_isExecuting = NO;
_isFinished = YES;
[self didChangeValueForKey:@"isExecuting"];
[self didChangeValueForKey:@"isFinished"];
if (_isCancelled == YES)
{
NSLog(@"** OPERATION CANCELED **");
}
}
私が見つけた例では、performSelectorOnMainThread:が使用されている理由がわかりません。これにより、操作が同時に実行されなくなります。
また、その行をコメントアウトすると、操作が同時に実行されます。ただし、isCancelled
を呼び出した場合でも、cancelAllOperations
フラグは変更されません。
さて、私はそれを理解しているので、あなたは2つの質問があります:
コードのコメントに表示される_performSelectorOnMainThread:
_セグメントが必要ですか?そのコードは何をしますか?
この操作を含むcancelAllOperations
でNSOperationQueue
を呼び出しても__isCancelled
_フラグが変更されないのはなぜですか?
これらを順番に処理しましょう。説明を簡単にするために、NSOperation
のサブクラスはMyOperation
と呼ばれると仮定します。あなたが誤解していることを説明してから、修正された例を示します。
ほとんどの場合、NSOperation
sでNSOperationQueue
sを使用し、コードから、それがあなたがしていることのように聞こえます。その場合、MyOperation
sはバックグラウンドで操作を実行するように明示的に設計されているため、-(BOOL)isConcurrent
メソッドが返すものに関係なく、NSOperationQueue
は常にバックグラウンドスレッドで実行されます。 。
そのため、デフォルトでは_-[NSOperation start]
_メソッドを呼び出すだけなので、通常_-main
_メソッドをオーバーライドする必要はありません。それはあなたがオーバーライドすべきメソッドです。デフォルトの_-start
_メソッドは、適切なタイミングでisExecuting
とisFinished
の設定を既に処理しています。
したがって、NSOperation
をバックグラウンドで実行する場合は、_-main
_メソッドをオーバーライドして、NSOperationQueue
に配置するだけです。
コード内の_performSelectorOnMainThread:
_により、MyOperation
のすべてのインスタンスが常にメインスレッドでタスクを実行します。スレッド上で一度に実行できるコードは1つだけなので、これは他のMyOperation
sを実行できないことを意味します。 NSOperation
とNSOperationQueue
の全体的な目的は、バックグラウンドで何かをすることです。
メインスレッドに強制するのは、ユーザーインターフェイスを更新するときだけです。 MyOperation
が終了したときにUIを更新する必要がある場合、thatは_performSelectorOnMainThread:
_を使用する必要があります。以下の例で、その方法を示します。
_-[NSOperationQueue cancelAllOperations]
_は_-[NSOperation cancel]
_メソッドを呼び出します。これにより、その後の_-[NSOperation isCancelled]
_の呼び出しがYES
を返します。 ただし、これを無効にするために2つのことを行いました。
NSOperationの_@synthesize isCancelled
_メソッドをオーバーライドするために_-isCancelled
_を使用しています。これを行う理由はありません。 NSOperation
はすでに_-isCancelled
_を完全に受け入れられる方法で実装しています。
独自の__isCancelled
_インスタンス変数をチェックして、操作がキャンセルされたかどうかを判断しています。 NSOperation
は、操作がキャンセルされた場合に_[self isCancelled]
_がYES
を返すことを保証します。 notは、カスタムセッターメソッドが呼び出されることを保証するものではなく、独自のインスタンス変数が最新であることも保証します。 _[self isCancelled]
_を確認する必要があります
ヘッダー:
_// MyOperation.h
@interface MyOperation : NSOperation {
}
@end
_
そして実装:
_// MyOperation.m
@implementation MyOperation
- (void)main {
if ([self isCancelled]) {
NSLog(@"** operation cancelled **");
}
// Do some work here
NSLog(@"Working... working....")
if ([self isCancelled]) {
NSLog(@"** operation cancelled **");
}
// Do any clean-up work here...
// If you need to update some UI when the operation is complete, do this:
[self performSelectorOnMainThread:@selector(updateButton) withObject:nil waitUntilDone:NO];
NSLog(@"Operation finished");
}
- (void)updateButton {
// Update the button here
}
@end
_
isExecuting
、isCancelled
、またはisFinished
で何もする必要がないことに注意してください。これらはすべて自動的に処理されます。 _-main
_メソッドをオーバーライドするだけです。とても簡単です。
(注意:技術的には、これは_-[MyOperation isConcurrent]
_がNSOperation
を上記で実装したように返すという意味で、「同時」NO
ではありません。ただし、バックグラウンドスレッドで実行されますisConcurrent
メソッドは、メソッドの意図をより正確に説明するため、実際には_-willCreateOwnThread
_という名前にする必要があります。 )
@BJHomerの優れた答えは更新に値します。
同時操作は、start
の代わりにmain
メソッドをオーバーライドする必要があります。
Appleドキュメント に記載されているとおり:
並行操作を作成する場合は、少なくとも次のメソッドとプロパティをオーバーライドする必要があります。
start
asynchronous
executing
finished
適切な実装もrequiresもcancel
をオーバーライドします。サブクラスthread-safeを作成し、必要なセマンティクスを正しく取得することも非常に注意が必要です。
したがって、完全かつ機能するサブクラスを Swiftで実装された提案 としてCode Reviewに配置しました。コメントや提案を歓迎します。
このクラスは、カスタム操作クラスの基本クラスとして簡単に使用できます。
私はこれが古い質問であることを知っていますが、私は最近これを調査していて、同じ例に遭遇し、同じ疑問を持っていました。
すべての作業をmainメソッド内で同期的に実行できる場合、並行操作は必要ありません。startをオーバーライドすることも、作業を行って、終了したらmainから戻ることもできません。
ただし、ワークロードが本質的に非同期である場合、つまりNSURLConnectionをロードする場合は、サブクラスを開始する必要があります。 startメソッドが戻ったとき、操作はまだ完了していません。 NSOperationQueueは、isFinishedフラグとisExecutingフラグに手動でKVO通知を送信した場合(たとえば、非同期URLの読み込みが完了または失敗した場合)にのみ、完了と見なされます。
最後に、開始したい非同期ワークロードがメインスレッドでリッスンする実行ループを必要とする場合、メインスレッドに開始をディスパッチしたい場合があります。作業自体は非同期であるため、同時実行性は制限されませんが、ワーカースレッドで作業を開始すると、適切な実行ループの準備ができていない場合があります。
「cancelled」プロパティの定義について(または「_ cancelled」iVARを定義) NSOperationサブクラス内では、通常は必要ありません。単に、USERがキャンセルをトリガーするときに、カスタムコードは、作業がfinishedになったことをKVOオブザーバーに常に通知する必要があります。つまり、isCancelled => isFinished。
特に、NSOperationオブジェクトが他の操作オブジェクトの完了に依存している場合、それらのオブジェクトのisFinishedキーパスを監視します。したがって、終了通知(キャンセルが発生した場合(が発生した場合)の生成に失敗すると、アプリケーション内の他の操作の実行が妨げられる可能性があります。
ところで、@ BJ Homerの答え:「isConcurrentメソッドはnamed -willCreateOwnThreadである必要があります」とLOTが意味をなします!
Start-methodをオーバーライドしない場合は、単にNSOperation-Objectのdefault-start-methodを手動で呼び出すだけなので、calling-thread自体はデフォルトで同期的です。そのため、NSOperation-Objectは非並行操作のみです。
ただし、start-method実装内でstart-methodをオーバーライドする場合、カスタムコードは別のスレッドなどを生成する必要があります。 「呼び出しスレッドのデフォルトは同期」という制限、したがってNSOperation-Objectを並行操作にすることで、その後非同期で実行できます。
ASIHTTPRequest をご覧ください。これは、サブクラスとしてNSOperation
の上に構築されたHTTPラッパークラスであり、これらを実装しているようです。 2011年半ばの時点で、開発者は新しいプロジェクトにASIを使用しないことを推奨していることに注意してください。