I'm integrating autocomplete on a search bar through Google Places API. And for the networking requests, I use AFNetworking.
I want to have only one request running at a time. So everytime I type a new letter, I do the following:
1 - Cancel the previous AFHTTPRequestOperation:
[self.currentGlobalSearchListRequest cancel];
NSLog(@"%@ cancelled", self.currentGlobalSearchListRequest);
2 - Start a new AFHTTPRequestOperation:
self.currentGlobalSearchListRequest = [searchService getGlobalSearchListItemsForString:searchString inRegion:region delegate:self];
NSLog(@"%@ started", self.currentGlobalSearchListRequest);
Here is the callback called when the request has finished running:
- (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];
}
Sometimes I hit the assertion, so I investigated a bit and discovered that most of the times
the failure block is called with an error code NSURLErrorCancelled
which is the expected behavior but sometimes, the success block is called!
This stack trace tells me that things in my code occurred in the right order
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>
Plus, I looked at the property isCancelled
on AFHTTPRequestOperation when success block is called but it gives me NO (!!!)
I know I could end up just testing instead of using NSAssert but would like to find the origin of the problem.
Have you ever encountered similar issues where cancel does not actually cancel the request? And then the success block is called instead of the failure block? Is this an issue to report to AFNetworking team? Thanks!
If it can help, the requests are freaking fast (Google autocomplete aPI is impressive...)
Looking into AFNetworkingCode
, see 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];
}
It seems that the only possibility for the race condition you are seeing is the case when the operation has already been finished ([self isFinished]
). Note that the interval between your cancel
and completion block is very small (10 ms). Maybe you could check isFinished
before trying to cancel the request?