简体   繁体   中英

NSOperation subclass vs NSInvocationOperation to replace a GCD operation

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! });

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM