为啥取消 AFHTTPRequestOperation 有时会遇到成功块?

Posted

技术标签:

【中文标题】为啥取消 AFHTTPRequestOperation 有时会遇到成功块?【英文标题】:Why cancelled AFHTTPRequestOperation sometimes hit the success block?为什么取消 AFHTTPRequestOperation 有时会遇到成功块? 【发布时间】:2013-12-22 09:24:59 【问题描述】:

我正在通过 Google Places API 在搜索栏上集成自动完成功能。对于网络请求,我使用 AFNetworking。

我希望一次只运行一个请求。所以每次我输入一个新字母时,我都会执行以下操作:

1 - 取消之前的 AFHTTPRequestOperation:

[self.currentGlobalSearchListRequest cancel];
NSLog(@"%@ cancelled", self.currentGlobalSearchListRequest);

2 - 启动一个新的 AFHTTPRequestOperation:

self.currentGlobalSearchListRequest = [searchService getGlobalSearchListItemsForString:searchString inRegion:region delegate:self];
NSLog(@"%@ started", self.currentGlobalSearchListRequest);

这是请求完成运行时调用的回调:

- (void)request:(PointSearchRequest *)request didLoadSearchListItems:(NSArray *)searchListItems 
    NSAssert(request == self.currentGlobalSearchListRequest, @"!!!callback from a request that should be cancelled!!!");
    [self.delegate searchListLoader:self didLoadGlobalSearchList:searchListItems];

有时我遇到了断言,所以我调查了一下,发现大多数时候 使用错误代码 NSURLErrorCancelled 调用失败块,这是预期的行为,但有时会调用成功块!

此堆栈跟踪告诉我代码中的事情以正确的顺序发生

2013-12-22 09:38:46.484 Point[63595:a0b] <PointSearchRequest: 0x18202b50> started
2013-12-22 09:38:46.486 Point[63595:a0b] <PointSearchRequest: 0x18202b50> cancelled
2013-12-22 09:38:46.487 Point[63595:a0b] <PointSearchRequest: 0x181a5dd0> started
2013-12-22 09:38:46.496 Point[63595:a0b] *** Assertion failure in -[SearchListLoader request:didLoadSearchListItems:], /Users/aurelienporte/Documents/Developpement/Perso/ios/Point/Point/Classes/Models/SearchListLoader.m:82
(lldb) po request
<PointSearchRequest: 0x18202b50>

另外,当调用成功块时,我查看了 AFHTTPRequestOperation 上的属性 isCancelled,但它给了我 NO (!!!) 我知道我最终可能只是测试而不是使用 NSAssert,但我想找出问题的根源。

你有没有遇到过类似的问题,cancel 并没有真正取消请求?然后调用成功块而不是失败块?这是要向 AFNetworking 团队报告的问题吗?谢谢!

如果它可以提供帮助,请求会非常快(Google 自动完成 API 令人印象深刻......)

【问题讨论】:

我期待这个。取消尚未触发其完成处理程序的已完成操作是不明确的;它可以触发完成处理程序或接受取消。我不会指望任何一个发生 100% 的时间。 【参考方案1】:

查看AFNetworkingCode,查看AFURLConnectionOperation.m, line 461

- (void)cancel 
    [self.lock lock];
    if (![self isFinished] && ![self isCancelled]) 
        [super cancel];

        if ([self isExecuting]) 
            [self performSelector:@selector(cancelConnection) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
        
    
    [self.lock unlock];

您看到的竞争条件的唯一可能性似乎是操作已经完成的情况([self isFinished])。请注意,cancel 和完成块之间的间隔非常小(10 毫秒)。也许您可以在尝试取消请求之前检查isFinished

【讨论】:

我看了这个,确实,在我的 NSAssert 触发的情况下,操作已经完成。谢谢。这意味着在 isFinished 设置为 YES 的时间和回调成功或失败块的时间之间存在延迟。我猜是因为这两个“时刻”不会出现在同一个线程中,对吧? @AurelienPorte 是的。 NSOperation 应该几乎立即调用它的completionBlock,但是在调用成功或失败块之前还有两个dispatch_async。每个dispatch_async 都会稍微推迟执行。这是因为 AFNetworking 使用了多个调度队列,并且执行必须经过所有这些队列才能到达您的成功块。【参考方案2】:

众所周知,一个线程函数虽然中途取消了线程,但它会走不,当它启动时,它会完成该方法。所以作为请求。也就是说,如果请求还在NSOperationQueue中,你可以取消它,但是只要你summit这个操作,并且block是copy,它就会回调。

如果不想接受回调,可以只在回调中告知请求是否被取消。

希望对你有所帮助。

【讨论】:

谢谢。我同意一般情况。这应该由 AFNetworking 库本身处理,因此应该调用我的失败块。

以上是关于为啥取消 AFHTTPRequestOperation 有时会遇到成功块?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 +e 取消而 -e 不取消?

为啥临时变量需要更改数组元素以及为啥需要在最后取消设置?

如果取消,为啥我的 SaveFileDialog 会再次显示?

为啥任务取消发生在调用者线程上?

为啥取消 AFHTTPRequestOperation 有时会遇到成功块?

取消 HttpClient 请求 - 为啥 TaskCanceledException.CancellationToken.IsCancellationRequested 为假?