私の iOS プログラムでは、次のことが起こります。ユーザーが入力すると、データベース ルックアップが開始されるスレッドに対してリクエストが送信されます。DB ルックアップが完了すると、アプリが結果を表示できるように、メイン スレッドで応答が発生します。
これはうまく機能しますが、ユーザーの入力が非常に高速である場合、進行中の要求がいくつかある可能性があります。最終的にはシステムが追いつくでしょうが、効率が悪いようです。
リクエストが開始された場合に、ルックアップがすでに進行中であることを検出できるように実装するためのきちんとした方法はありますか?
以下にコメントを追加したサンプル ソリューション
ソリューションのプロパティを示す小さなサンプル プロジェクトのビュー コントローラーの本体を次に示します。入力すると、次のような出力が得られる場合があります。
2012-11-11 11:50:20.595 TestNsOperation[1168:c07] Queueing with 'd'
2012-11-11 11:50:20.899 TestNsOperation[1168:c07] Queueing with 'de'
2012-11-11 11:50:21.147 TestNsOperation[1168:c07] Queueing with 'det'
2012-11-11 11:50:21.371 TestNsOperation[1168:c07] Queueing with 'dett'
2012-11-11 11:50:21.599 TestNsOperation[1168:1b03] Skipped as out of date with 'd'
2012-11-11 11:50:22.605 TestNsOperation[1168:c07] Up to date with 'dett'
この場合、最初にエンキューされた操作はスキップされます。これは、作業の長い部分を実行している間に古くなったと判断されるためです。次の 2 つのキューに入れられた操作 ('de' と 'det') は、実行が許可される前に取り消されます。最後の最終操作は、実際にすべての作業を完了する唯一の操作です。
[self.lookupQueue cancelAllOperations] 行をコメントアウトすると、代わりに次の動作になります。
2012-11-11 11:55:56.454 TestNsOperation[1221:c07] Queueing with 'd'
2012-11-11 11:55:56.517 TestNsOperation[1221:c07] Queueing with 'de'
2012-11-11 11:55:56.668 TestNsOperation[1221:c07] Queueing with 'det'
2012-11-11 11:55:56.818 TestNsOperation[1221:c07] Queueing with 'dett'
2012-11-11 11:55:56.868 TestNsOperation[1221:c07] Queueing with 'dette'
2012-11-11 11:55:57.458 TestNsOperation[1221:1c03] Skipped as out of date with 'd'
2012-11-11 11:55:58.461 TestNsOperation[1221:4303] Skipped as out of date with 'de'
2012-11-11 11:55:59.464 TestNsOperation[1221:1c03] Skipped as out of date with 'det'
2012-11-11 11:56:00.467 TestNsOperation[1221:4303] Skipped as out of date with 'dett'
2012-11-11 11:56:01.470 TestNsOperation[1221:c07] Up to date with 'dette'
この場合、キューに入れられたすべての操作は、実行がスケジュールされる前に新しい操作がキューに入れられたとしても、作業の長さの部分を実行します。
@interface SGPTViewController ()
@property (nonatomic, strong) NSString* oldText;
@property (strong) NSOperationQueue *lookupQueue;
@end
@implementation SGPTViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.oldText = self.source.text;
self.lookupQueue = [[NSOperationQueue alloc] init];
self.lookupQueue.maxConcurrentOperationCount = 1;
}
- (void)textViewDidChange:(UITextView *)textView
{
// avoid having a strong reference to self in the operation queue
SGPTViewController * __weak blockSelf = self;
// you can cancel existing operations here if you want
[self.lookupQueue cancelAllOperations];
NSString *outsideTextAsItWasWhenStarted = [NSString stringWithString:self.source.text];
NSLog(@"Queueing with '%@'", outsideTextAsItWasWhenStarted);
[self.lookupQueue addOperationWithBlock:^{
// do stuff
NSString *textAsItWasWhenStarted = [NSString stringWithString:outsideTextAsItWasWhenStarted];
[NSThread sleepForTimeInterval:1.0];
if (blockSelf.lookupQueue.operationCount == 1) {
// do more stuff if there is only one operation on the queue,
// i.e. this one. Operations are removed when they are completed or cancelled.
// I should be canceled or up to date at this stage
dispatch_sync(dispatch_get_main_queue(), ^{
if (![textAsItWasWhenStarted isEqualToString:self.source.text]) {
NSLog(@"NOT up to date with '%@'", textAsItWasWhenStarted);
} else {
NSLog(@"Up to date with '%@'", textAsItWasWhenStarted);
}
});
} else {
NSLog(@"Skipped as out of date with '%@'", textAsItWasWhenStarted);
}
}];
}