iOS保持异步查询顺序

时间:2015-02-21 21:20:32

标签: ios objective-c search grand-central-dispatch dispatch-async

    - (void)loadItems {
    AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
    [manager.requestSerializer setValue:@"text/html" forHTTPHeaderField:@"Content-Type"];
    [manager GET:@"someurl"
      parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
              [self reloadData];
          }

      } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
          NSLog(@"Error: %@", error);
      }];
}

- (void)textFieldDidChange {
    [_filteredArray removeAllObjects];
    [self loadItems];
}

我试图通过每次角色更改时进行API调用来实现即时搜索。由于前几个调用的字母数量较少,它们会返回更多结果,这使得前几个异步调用的结束速度比最后几个慢,这意味着如果我快速输入hello,我将最终获得搜索结果对于h而不是整个单词,因为最后一次完成调用是h的那个。我需要保持这些调用的顺序,并确保不覆盖最后一个查询。我知道我必须使用队列结构。但是,在textFieldDidChange中执行此类操作似乎无效:

dispatch_group_async(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
    [self loadItems];
});
dispatch_group_notify(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
    [self reloadData];
});

我想我需要使用某种dispatch_group_enter(group);dispatch_group_leave(group);的组合。但是我仍然无法拨打电话来停止覆盖最后一个电话。我不确定是否还有一种方法可以用最后一个取消所有其他已启动的呼叫,或者我是否必须等待所有这些呼叫按顺序完成。任何帮助,将不胜感激。

3 个答案:

答案 0 :(得分:0)

这是我的解决方案。我刚刚使用了一个我传递给loadItems函数的计数器。当该计数器更新时,异步调用仍然有自己的值,所以我只比较两者,如果异步调用的计数器等于最新的计数器,请确保只有reloadData

- (void)loadItems:(int)queryInt {
    AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
    [manager.requestSerializer setValue:@"text/html" forHTTPHeaderField:@"Content-Type"];
    [manager GET:@"someurl"
      parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
              if (searchQueryCounter - 1 == queryInt) {
                  [self reloadDatawithAnimation];
              } else {
                  return;
              }
      } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
          NSLog(@"Error: %@", error);
      }];
}

- (void)textFieldDidChange {
    [_filteredArray removeAllObjects];
    [self loadItems:searchQueryCounter];
    searchQueryCounter = searchQueryCounter + 1;
}

答案 1 :(得分:0)

您可以通过取消先前的请求来解决此问题,不仅可以阻止先前的请求报告结果,还可以确保系统资源不会被不再需要的请求使用:

@interface ViewController () <UITextFieldDelegate>

@property (nonatomic, strong) AFHTTPRequestOperationManager *manager;
@property (nonatomic, weak) NSOperation *previousOperation;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    // no need to instantiate new request operation manager each time;
    // do it at some logical point of initialization (e.g. in `viewDidLoad`
    // for view controllers, etc.).

    self.manager = [AFHTTPRequestOperationManager manager];
    [self.manager.requestSerializer setValue:@"text/html" forHTTPHeaderField:@"Content-Type"];
}

- (void)loadItems {
    [self.previousOperation cancel];
    typeof(self) __weak weakSelf = self;  // probably should use weakSelf pattern, too
    NSOperation *operation = [self.manager GET:@"someurl" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
        [weakSelf reloadData];
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        if ([error.domain isEqualToString:NSURLErrorDomain] && [error code] != NSURLErrorCancelled) {
            NSLog(@"Error: %@", error);
        }
    }];
    self.previousOperation = operation;
}

- (void)textFieldDidChange {
    [_filteredArray removeAllObjects];
    [self loadItems];
}

@end

答案 2 :(得分:0)

上周我实际上处理了一个非常类似的问题,并提出了一种你可能会觉得有用的方法。

我使用performSelector:withObject:afterDelay稍微延迟提交每个请求(我一直在尝试0.5到1.0秒之间的值。从.66到.75的某个地方似乎是一个很好的折衷值。)

对于每个新请求,我取消先前挂起的performSelector调用。这样,在用户短时间内停止输入之前,不会发送任何内容。它并不完美,但它减少了对单词片段无用查询的数量。代码看起来像这样:

static NSString *methodWord = nil;
[[self class] cancelPreviousPerformRequestsWithTarget: self
                                             selector: @selector(handleWordEntered:)
                                               object: methodWord];
methodWord = word;
[self performSelector: @selector(handleWordEntered:)
           withObject: methodWord
           afterDelay: .667];

方法handleWordEntered:实际上将请求发送到服务器。

如果用户键入一个字母,然后在不到2/3秒内输入另一个字母,则先前的待处理请求将被取消,并且新请求将设置为稍后触发2/3秒。只要用户每2/3秒输入一次字母,就不会发送任何内容。一旦用户暂停超过2/3秒,就会发送一个请求。一旦performSelector:withObject:afterDelay触发,它就不能再被取消,以便请求进入网络并解析回复。