可取消的iOS异步操作集合及进度报告

4
假设我使用另一个SDK(我无法控制)和一个异步导入1个文件的API,完成后调用完成回调函数。 以下是示例API。
func importFile(filePath: String, completion: () -> Void)

我需要使用此API导入10个文件(一个接一个),但需要实现可取消性。例如,在成功导入文件1、2、3后,当正在导入文件4时,我想能够取消所有操作(导入这10个文件),这样文件4将完成(因为它已经开始了),但是文件5-10不再导入。
另外,我还需要报告导入的进度。当文件1成功导入后,我应该报告10%的进度(已完成10中的1)。
我考虑使用带有10个NSOperation的NSOperationQueue,但似乎很难进行进度报告。

请检查我的答案并告诉我这是否符合您的要求。 - KrishnaCA
实际上,我认为我们也可以使用dispatch_suspend来解决这个问题。你是在寻找仅基于NSOperation的解决方案吗? - KrishnaCA
我对我的答案进行了编辑,以便回答您所有的疑问。我恳请您看一下。 - KrishnaCA
3个回答

1

所以,我相信这是你从问题中想要的内容:

  1. 逐个导入n个文件到队列中
  2. 在每个文件导入时报告进度
  3. 有能力在操作进行中取消操作

可以使用以下方式使用NSOperationQueueNSBlockOperation实现。

  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];
    

我自己尝试了这段代码,它运行良好。欢迎提出改进意见,让它变得更好 :)


看起来不错。但是如果根据问题描述importFile是异步的,你会怎么做呢?另外,当所有操作完成后,关于完成回调,你如何满足这个要求? - Dj S
我马上会加进去。 - KrishnaCA
如果进度达到100,那么就意味着成功了,对吧?在这种情况下,当您有手动取消操作的选项时,失败报告变得困难。小疑问:如果导入的文件中有一个失败了,我们应该继续还是取消所有操作? - KrishnaCA
另外,由于您的importFile函数是异步的,因此您可以使用https://gist.github.com/msealand/b90e621ad5420bc4f2bf而不是普通的NSOperationBlock。 - KrishnaCA
我明白了,你建议在NSOperation的子类中采用异步实现方式。谢谢! - Dj S
我已经编辑了我的回答,以使其回答所有您的查询。请尝试一下。 - KrishnaCA

0

NSOperationQueue提供了一个很好的面向对象的抽象,这是我会选择的方式。

  • 创建一个名为importQueueNSOperationQueue

对于每个导入:

  • 创建一个NSOperation
  • 存储操作以便稍后可以到达和取消它,例如在字典[ImportNumber:NSOperation]中
  • 将操作添加到importQueue

关于进度状态:
选项1: NSOperationQueue有一个叫做operations的属性,你可以观察它的数量。(importQueue.operations.count)。
选项2: NSOperation提供了完成块。当排队操作时,您可以增加计数器,并在完成块或取消时减少它。

更多阅读:苹果文档, 异步NSOperation的原因和方法, 不错但有点陈旧的教程


1
你将如何为异步API(如问题描述中所述)实现NSOperation? - Dj S
具体实现会太长并且不适合在SO上提供。如果您在阅读教程后有问题,请告诉我们,也许可以在新的问题中针对特定的新问题进行讨论。 - shallowThought
抱歉,我不是在要求具体的实现。简短的回答就可以了。问题明确指出API(来自第三方SDK)是异步的,而你的答案使用了同步API。 - Dj S
NSOperation可以同步(或非并发)和异步(或并发)运行。所有这些都在提到的教程中得到了很好的解释。 - shallowThought
你提到的教程中的NSOperations(ImageDownloader和ImageFiltration)仅使用同步API。你所指的异步API是什么? - Dj S
我添加了更多的链接。希望这有帮助。 - shallowThought

0

我认为你应该在你的操作中添加依赖项。 这个想法是这样的:

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

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

然后你的操作子类应该以这种方式覆盖取消方法

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

进度报告和完成回调怎么样? - Dj S
在你的操作中,你可以调用进度更新委托。 - iGenio

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接