Why cancelled AFHTTPRequestOperation sometimes hit the success block?

943 views Asked by At

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...)

2

There are 2 answers

2
Sulthan On BEST ANSWER

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?

1
CodeToPlay On

As we all know, a thread function will go no although you cancel the thread in the midway, when it start, it will complete the method. So as the request. That is, if the request is still in the NSOperationQueue, you can cancel it, but as long as you summit the operation,and the block is copy,it will call back.

If you don't want to accept the call back, you can just in the call back to tell whether the request is canceled.

Hope that it will help you.