Cancellable set of asynchronous operations with progress reporting in iOS

妖精的绣舞 提交于 2019-12-19 09:05:29

问题


Suppose that I use another SDK (which I do not have control of) with an API that imports 1 file asynchronously, and calls a completion callback on completion. The following is an example API.

func importFile(filePath: String, completion: () -> Void)

I need to import 10 files (one by one) using this API, but I need it to be cancellable, e.g. after Files 1,2,3 has been successfully imported, while File 4 is being imported, I want to be able to cancel the whole set of operations (import of the 10 Files), such that File 4 will finish (since it already started), but Files 5-10 will not be imported anymore.

In addition, I also need to report progress of the import. When File 1 has been imported successfully, then I should report progress of 10% (1 out of 10 has been finished).

How can I achieve this?

I am considering using NSOperationQueue with 10 NSOperations, but it seems that progress reporting will be difficult.


回答1:


So, I believe that this is the following you want from your question:

  1. Import n files one by one in a queue
  2. Report progress when each file is imported
  3. Ability to cancel operation in the middle

It can be achieved using NSOperationQueue and NSBlockOperation in the following way.

  1. Create AsyncBlockOperation and NSOperationQueue+AsyncBlockOperation classes from code given in answer for the following StackOverflow question: NSOperation wait until asynchronous block executes
  2. Import both the classes into the file they are going to be used
  3. Create an operation queue

    NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
    operationQueue.maxConcurrentOperationCount = 1;
    operationQueue.name = @"com.yourOrganization.yourProject.yourQueue";
    
  4. Create a function which gives you a callback for getting progress

    - (void)importFilesFromFilePathArray:(NSArray *)pathsArray
                        inOperationQueue:(NSOperationQueue *)operationQueue
                            withProgress:(void (^)(CGFloat progress))progressBlock {
    
      }
    
  5. Inside the function defined in 2, use NSBlockOperation for performing your operations in the NSOperationQueue

    for (int i = 0; i < pathsArray.count; i++) {
    
        [operationQueue addAsyncOperationWithBlock:^(dispatch_block_t completionHandler) {
            [self importFile:(NSString *)[pathsArray objectAtIndex:i] completion:^(bool completion) {
                [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                    CGFloat progress = (100 * (float)(i+1)/pathsArray.count);
                    progressBlock(progress);
                    if (progress == 100) {
                       successBlock(YES);
                    }
                 }];
                 completionHandler();
             }];
         }];
    }
    
  6. For cancelling operations, we can simply use the operationQueue that we created in the 1st step

    [operationQueue cancelAllOperations];
    

I tried this code myself. It's working well. Feel free to suggest improvements to make it better :)




回答2:


I think you should add dependencies on your operation. The idea is this:

1Op = NSOperation..
2Op = NSOperation..
.
.
10Op = NSOperation..

10Op.addDependency(9Op)
9Op.addDependency(8Op) and so on...

Then your operation subclass should override the cancel method in this way

cancel() {
    super.cancel()
    for dep in dependencies {
        dep.cancel()
    }
}



回答3:


NSOperationQueue offers a nice object oriented abstaraction and is the way I would go.

  • Create a NSOperationQueuenamed importQueue

For each import:

  • Create a NSOperation
  • Store the operation to reach and possibly cancle it later on, for instance in a dictionary [ImportNumber:NSOperation]
  • Add the operation to importQueue

In regards to the progress state:

Option 1:

NSOperationQueue has a property called operations, whichs count you are able to observe. (importQueue.operations.count).

Option 2:

NSOperation offers completion blocks. You can increase a counter when queueing the operation and decrease it within the completion block or when cancelling.


Further reading: Apple documentation, asynchronous-nsoperation-why-and-how, nice but oldish tutorial.



来源:https://stackoverflow.com/questions/40265055/cancellable-set-of-asynchronous-operations-with-progress-reporting-in-ios

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!