Question

I am new to NSOperationQueue, but in a current project I need a way to cancel an asynchronous operation when it times out. My original code uses GCD and works. Here it is:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0),^{
    rg = [[RiverGauge alloc] initWithStationInfo:[station id] forHistoryInHours:48 inThisFormat:nil];
    dispatch_async(dispatch_get_main_queue(), ^{
        [hud removeFromSuperview];
        UIStoryboard *sb = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
        GaugeViewController *vc = [sb instantiateViewControllerWithIdentifier:@"GaugeDetailViewController"];
        [vc setRiverGauge:rg];
        [self.navigationController pushViewController:vc animated:YES];
    });
});

RiverGauge is a network operation that could take a long time, especially if the user is in rural areas. Most posts I've seen suggest moving this into an NSOperationQueue. I have done so as follows:

NSOperationQueue *queue = [NSOperationQueue new];
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(getGaugeDetail:) object:[station id]];
[queue addOperation:op];

In the getGaugeDetail method (in the selector parameter) I have the following code:

RiverGauge *rg = [[RiverGauge alloc] initWithStationInfo:gaugeIdentifier forHistoryInHours:48 inThisFormat:nil];
UIStoryboard *sb = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
GaugeViewController *vc = [sb instantiateViewControllerWithIdentifier:@"GaugeDetailViewController"];
[vc setRiverGauge:rg];
[self.navigationController pushViewController:vc animated:YES];

When doing this operation using GCD, it responds quickly with a fast internet connection. However, when using NSOperationQueue, the same operation takes several seconds. The returned data is accurate, however. Again, I'm not very familiar with NSOperationQueue. Any suggestions?

Thanks!

Was it helpful?

Solution

In your code when using an NSOperationQueue, it looks like you're making your UI changes in the NSOperationQueue, which would presumably be performing these on a thread other than the main thread. Sometimes when you interact with UIKit elements off the main thread, they either don't work correctly or there's a noticeable delay before the results of them appear. So my guess is that you're seeing the effects of making UI changes on the wrong thread.

Try performing the UIKit stuff back on the main thread and see if it seems to fix your issue. You can probably continue to use GCD to do that.

OTHER TIPS

Instead of

[self.navigationController pushViewController:vc animated:YES]; 

you can try this:

[self.navigationController performSelectorOnMainThread:@selector(someMethod) withObject:<#(id)#> waitUntilDone:NO];

-(void)someMethod
{
//pushViewController:animated: //and other stuffs
}

Also if you want to cancel the operation, it would be better if you use NSOperation instead of NSOperation instead of NSInvocationOperation. You can create a subclass of NSOperation and you can perform all the other stuff apart from the UI update there itself. It has a property "cancel". You can use it along with the NSMutableURLRequest if you want it to be cancelled for time out.

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:self.downloadURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:30.0];

I dont know exactly what are you trying to do in the second thread apart from the main thread but the only thing that you have to keep in mind is that you have to separate the main thread from the other threads. Whenever you are dealing with the UI or updating the UI, you have to do all that stuff in the main thread. The rest of the manipulations and background stuff can be handled by the separate thread by using NSOperationQueue. Otherwise, your UI might freeze. Please let me know if it works. Thanks :) :)

First of all, you don't need to move this into NSOperationQueue from GCD. It doesn't make sense. The problem is that RiverGauge -initWithStationInfo:forHistoryInHours:inThisFormat: method itself.

RiverGauge is a network operation that could take a long time, especially if the user is in rural areas.

If you want to cancel this method, you have to implement something for canceling in the method, or rewrite completely. Separating a network operation from the method and using asynchronous API like NSURLConnection + sendAsynchronousRequest:queue:completionHandler: is a good idea. You can cancel NSURLConnection asynchronously at anytime.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top