サブクラス化して、同時にキャンセル可能である
-
27-09-2019 - |
質問
サブクラスの方法についての良いドキュメントを見つけることができません NSOperation
同時に、またキャンセルをサポートすること。 Apple Docsを読みましたが、「公式」の例を見つけることができません。
これが私のソースコードです:
@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:
コードにコメントに表示されるセグメント?そのコードは何をしますか?なぜですか
_isCancelled
あなたが電話するとき、フラグは変更されませんcancelAllOperations
にNSOperationQueue
これにはこの操作が含まれていますか?
これらを順番に扱いましょう。私はあなたのサブクラスを想定します NSOperation
呼ばれています MyOperation
, 、説明のためだけに。あなたが誤解していることを説明し、修正された例を挙げます。
1. nsoperationsを同時に実行します
ほとんどの場合、使用します NSOperation
sと NSOperationQueue
, 、そしてあなたのコードから、それはあなたがしていることのように聞こえます。その場合、あなた MyOperation
何に関係なく、常に背景スレッドで実行されます -(BOOL)isConcurrent
メソッドは戻ります NSOperationQueue
Sは、バックグラウンドで操作を実行するように明示的に設計されています。
そのため、一般的にオーバーライドする必要はありません -[NSOperation start]
メソッド、デフォルトでは、単に呼び出すだけです -main
方法。それがあなたがオーバーライドすべき方法です。デフォルト -start
メソッドはすでに設定を処理しています isExecuting
と isFinished
適切な時期にあなたのために。
だからあなたが欲しいなら NSOperation
バックグラウンドで実行するには、単にオーバーライドします -main
方法とそれをに置きます NSOperationQueue
.
performSelectorOnMainThread:
コードでは、すべてのインスタンスを引き起こします MyOperation
メインスレッドで常にタスクを実行します。一度にスレッドで実行できるのは1つのコードのみなので、これは他にないことを意味します MyOperation
Sが実行されている可能性があります。の全体的な目的 NSOperation
と NSOperationQueue
バックグラウンドで何かをすることです。
メインスレッドに物事を強制したいのは、ユーザーインターフェイスを更新するときだけです。あなたがあなたのときにUIを更新する必要がある場合 MyOperation
仕上げ、 それ 使用する必要があるときです performSelectorOnMainThread:
. 。以下の私の例でそれを行う方法を示します。
-[NSOperationQueue cancelAllOperations]
呼び出します -[NSOperation cancel]
メソッドは、後続の呼び出しを引き起こします -[NSOperation isCancelled]
戻る YES
. でも, 、あなたはこれを効果的ではないものにするために2つのことをしました。
あなたは使用しています
@synthesize isCancelled
nsoperationのオーバーライド-isCancelled
方法。これを行う理由はありません。NSOperation
すでに実装しています-isCancelled
完全に受け入れられる方法で。あなたはあなた自身をチェックしています
_isCancelled
インスタンス変数操作がキャンセルされたかどうかを判断します。NSOperation
それを保証します[self isCancelled]
戻りますYES
操作がキャンセルされている場合。します いいえ カスタムセッターメソッドが呼び出されることも、独自のインスタンス変数が最新であることも保証します。チェックする必要があります[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
方法。そんなに簡単です。
(注:技術的には、これは「同時」ではありません NSOperation
, 、その意味で -[MyOperation isConcurrent]
戻ってきます NO
上記で実装されています。しかし、それ 意思 背景スレッドで実行されます。 isConcurrent
メソッドは本当に名前が付けられている必要があります -willCreateOwnThread
, 、それは方法の意図のより正確な説明であるためです。)
他のヒント
@bjhomerの優れた答えは、更新に値します。
同時操作はオーバーライドする必要があります start
の代わりに main
.
で述べられているように Appleドキュメント:
同時操作を作成している場合は、少なくとも次の方法とプロパティをオーバーライドする必要があります。
start
asynchronous
executing
finished
適切な実装も 必要 オーバーライドする cancel
同じように。サブクラスを作る スレッドセーフ また、必要なセマンティクスを正しく取得することも非常に難しいです。
したがって、私は完全で働くサブクラスを、 Swiftで実装された提案 コードレビューで。コメントと提案は大歓迎です。
このクラスは、カスタムオペレーションクラスのベースクラスとして簡単に使用できます。
私はこれが古い質問であることを知っていますが、私は最近これを調査しており、同じ例に遭遇し、同じ疑問がありました。
すべての作業をメインメソッド内で同期して実行できる場合、同時操作は必要ありません。また、オーバーライドスタートはなく、作業を行い、完了したらメインから戻ります。
ただし、ワークロードが本質的に非同期である場合 - つまり、nsurlconnectionのロードをロードする必要があります。開始方法が戻ってきたとき、操作はまだ終了していません。 KVO通知をIsfinishedおよびISExecute Flagsに手動で送信する場合にのみ、NSOPerationQueueによって完成したと見なされます(たとえば、非同期のURLの読み込みが終了または失敗すると)。
最後に、メインスレッドを開始したいAsyncワークロードがメインスレッドでリスニングをリスニングする必要がある場合、メインスレッドにスタートを派遣することをお勧めします。作業自体は非同期であるため、同時性は制限されませんが、ワーカースレッドで作業を開始することは適切なRunloopの準備ができていない場合があります。
を見てみましょう asihttprequest. 。上に構築されたHTTPラッパークラスです NSOperation
サブクラスとして、これらを実装しているようです。 2011年半ばの時点で、開発者は新しいプロジェクトにASIを使用しないことを推奨していることに注意してください。
定義について」キャンセル「プロパティ(または定義する」_キャンセル「ivar)nsoperationサブクラスの内部、通常は必要ありません。単に、ユーザーがキャンセルをトリガーした場合、カスタムコードは常にKVOオブザーバーにあなたの操作が今であることを通知する必要があります 終了した その仕事で。言い換えると、 iscancelled => isfinished。
特に、nsoperationオブジェクトが他の操作オブジェクトの完了に依存している場合、それらのオブジェクトの設立されたキーパスを監視します。仕上げ通知を生成しない(キャンセルの場合に発生します)したがって、アプリケーションで他の操作の実行を防ぐことができます。
ところで、@BJホーマーの答え:「isConcurrentメソッドは本当に 名前が付けられた-WillCreateOwnThread「非常に理にかなっています!
Start-Methodをオーバーライドしない場合は、手動でnsoperation-Objectのデフォルトスタートメソッドをコールするだけで、呼び出しスレッド自体がデフォルトで同期するためです。したがって、nsoperation-objectは、非伝統操作にすぎません。
ただし、Start-Methodの内部をオーバーライドする場合は、Custom-Codeの内部 別のスレッドを生成する必要があります…など、「呼び出しスレッドデフォルトが同期」の制限を正常に破るため、Nsoperation-Objectが同時操作になるようにすると、その後非同期に実行できます。
このブログ投稿:
http://www.dribin.org/dave/blog/archives/2009/09/13/snowy_concurrent_operations/
必要な理由を説明してください。
if (![NSThread isMainThread])
{
[self performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:NO];
return;
}
あなたの中で start
方法。