Reputation: 6367
I am somewhat new to NSOperation. I have an existing app with a potentially long running task. The task downloads data via a web service. The task could take some time to complete especially in rural areas where connectivity is slow. We need to update this app so the user can cancel the download, or it can timeout on its own.
Currently we are using GCD to perform the download, but a GCD operation cannot be cancelled. The class that downloads the data preferably will not be modified, but rather just treated as any long running operation. The initializer of that object gets the web service data and returns when it is complete.
I am not sure whether to use NSInvocationOperation or a subclass of NSOperation. The task is pretty simple, just to allow the cancellation of an operation.
MBProgressHUD is used as an activity indicator and has a gesture recognizer that will be used so the user can tap it to cancel the operation. Additionally, the operation should provide for a timeout. Changes to the UI will occur when the operation has finished.
Current GCD code:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0),^{
// long running operation
GaugeList *rg = [[GaugeList alloc] initWithStationInfo:[station id] forHistoryInHours:48 inThisFormat:nil];
dispatch_async(dispatch_get_main_queue(), ^{
UIStoryboard *sb = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
GaugeViewController *vc = [sb instantiateViewControllerWithIdentifier:@"GaugeDetailViewController"];
[vc setRiverGauge:rg];
[self.navigationController pushViewController:vc animated:YES];
[hud removeFromSuperview];
});
I am looking for a quick solution, but most examples I've seen are quite lengthy and might require significant refactoring. Can anyone point to an example that offers a solution to this? Thx! });
Upvotes: 1
Views: 242
Reputation: 18132
NSInvocationOperation
does not support cancellation. It simply gives you the ability to execute an existing method on an operation queue. If you want cancellation, you have to write an NSOperation
subclass. Cancellation support does require some work on your part though, because in the -main
method of the NSOperation
subclass (where you put your long running work), you need to continuously check the isCancelled
property to determine when to return from the method and end the operation (ie. if you ignore the isCancelled
flag and the code in -main
continues to run, cancellation will have no effect).
So essentially, you want to create an NSOperation
subclass with a delegate that you can call a method on (on the main queue) to notify that the long running background operation has completed, and then have your long running code in -main
with regular checks of -isCancelled
to make sure you respond quickly to cancellation.
Something like this:
@protocol MyOperationDelegate <NSObject>
@required
- (void)myOperationDidComplete:(MyOperation *)operation;
@end
@interface MyOperation : NSOperation
@property (nonatomic, assign) id<MyOperationDelegate> delegate
@end
@implementation MyOperation
- (void)main {
@autoreleasepool {
// Long running work
// Regularly check -isCancelled throughout the work, like this:
if ([self isCancelled]) {
// Cleanup
return;
}
// At the end of the work
dispatch_async(dispatch_get_main_queue(), ^{
[self.delegate myOperationDidComplete:self];
});
}
}
@end
Upvotes: 1