如何在iOS中实现只处理最新请求的工作队列? [英] How to implement work queue in iOS where only newest request is handled?

查看:227
本文介绍了如何在iOS中实现只处理最新请求的工作队列?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的iOS程序中,发生以下情况:当用户键入时,请求被触发到启动数据库查询的线程。当数据库查找完成后,在主线程上触发一个响应,以便应用程序可以显示结果。

In my iOS program the following happens: As the user types, a request is fired off to a thread where a database lookup is initiated. When the DB lookup is done, a response is fired off on the main thread so the app can display the results.

这很好,除非用户输入真的快,在飞行中可能有几个请求。最终系统会赶上来,但似乎效率不高。

This works great, except that if the user types really fast, there may be several requests in-flight. Eventually the system will catch up, but it seems inefficient.

有一个整洁的方法来实现它,所以如果请求被启动,我可以检测到查找是已经在进行中,请求应该存储为可能是最新的,超过了飞行中的一个。

Is there a neat way to implement it so that if a request is initiated, I can detect that a lookup is already in progress, and the request should instead be stored as "potentially newest that superceeds the one in-flight"?

示例解决方案下面添加的评论

SAMPLE SOLUTION WITH COMMENTS ADDED BELOW

这是小型示例项目的视图控制器的主体,它说明了解决方案的属性。当输入时你可能得到这样的输出:

Here's the body of a view controller for a small sample project that illustrates the properties of the solution. When typing you might get output like this:

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'

在这种情况下,将跳过第一个入列操作,因为它确定在执行其工作的冗长部分时已过时。以下两个入队操作('de'和'det')在它们甚至被允许执行之前被取消。

In this case, the first enqueued operation is skipped because it determines that it has become outdated while performing the lengthy part of its work. The two following enqueued operations ('de' and 'det') are cancelled before they are even allowed to execute. The final final operation is the only one to actually finish all its work.

如果你注释掉[self.lookupQueue cancelAllOperations]行,你会得到这个行为:

If you comment out the [self.lookupQueue cancelAllOperations] line, you get this behavior instead:

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'

在这种情况下,所有排队的操作将执行其工作的长度部分,即使更新的操作在它被调度之前执行。

In this case, all the enqueued operation will perform the length part of their work, even though a newer operation has been enqueued after it before it has even been scheduled for execution.

@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);
        }
    }];
}


推荐答案

我喜欢NSOperationQueue

I like NSOperationQueue for situations like this.

@interface ClassName ()
...
// atomic since it does not specify nonatomic
@property (strong) NSOperationQueue *lookupQueue;
...
@end

- (id)init
{
    ...
    lookupQueue = [[NSOperationQueue alloc] init];
    lookupQueue.maxConcurrentOperationCount = 1;
    ...
}

- (void)textFieldDidChange
{
    // avoid having a strong reference to self in the operation queue
    ClassName * __weak blockSelf = self;

    // you can cancel existing operations here if you want
    // [lookupQueue cancelAllOperations];

    [lookupQueue addOperationWithBlock:^{
        // do stuff
        ...
        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.
        }
    }];
}



编辑:只需要注意,你需要使用[[NSOperationQueue mainQueue] addOperationWithBlock :]或类似的更新GUI或运行任何其他代码,必须在主线程,从块参数内的[lookupQueue addOperationWithBlock:]。

Just a note, you need to use [[NSOperationQueue mainQueue] addOperationWithBlock:] or similar to update the GUI or run any other code that must go on the main thread, from inside the block argument to [lookupQueue addOperationWithBlock:].

这篇关于如何在iOS中实现只处理最新请求的工作队列?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆