首页 > 解决方案 > 从异步完成处理程序中中断“for”循环

问题描述

我的应用程序(Swift 5)使用循环内的异步完成处理程序将文件发送到服务器,并使用for信号量来确保同时只发送一个文件。

如果上传失败或出现异常,我想打破循环以显示错误消息。

我的代码:

let group = DispatchGroup()
let queue = DispatchQueue(label: "someLabel")
let sema = DispatchSemaphore(value: 0)

queue.async {
    for (i,item) in myArray.enumerated() {
        group.enter()

        do {
            let data = try Data(contentsOf: item.url)

            ftpProvider.uploadData(folder: "", filename: item.filename, data: data, multipleFiles: true, completion: { (success, error) in
                if success {
                    print("Upload successful!")
                } else {
                    print("Upload failed!")
                    //TODO: Break here!
                }
            group.leave()
            sema.signal()
        })
        sema.wait()
        } catch {
            print("Error: \(error.localizedDescription)")
            //TODO: Break here!
        }
    }
}

group.notify(queue: queue) {
    DispatchQueue.main.async {
        print("Done!")
    }
}

添加 abreak会给我一条错误消息:

未标记的“break”仅允许在循环或开关内使用,需要标记的 break 才能退出 if 或 do

向循环 ( myLoop: for (i,s) in myArray.enumerated()) 添加标签也不起作用:

使用未解决的标签“myLoop”

break self.myLoop也失败了。

在第一个文件的上传完成之前添加一个print权利group.enter()证明循环不仅仅是在第一个文件的上传完成之前完成,而是在“上传成功”/“上传失败”之前打印文本(正如它应该的那样)。由于这种破坏应该是可能的:

如何打破循环,以便从内部显示错误对话框group.notify

标签: swiftforeachbreakcompletionhandler

解决方案


我建议的方法是基于此问题AsynchronousOperation的已接受答案中提供的。

创建类,复制代码并创建一个AsynchronousOperation包含异步任务和完成处理程序的子类

class FTPOperation: AsynchronousOperation {

    var completion : ((Result<Bool,Error>) -> Void)?
    let item : Item // replace Item with your custom class

    init(item : Item) {
        self.item = item
    }

    override func main() {
        do {
            let data = try Data(contentsOf: item.url)
            ftpProvider.uploadData(folder: "", filename: item.filename, data: data, multipleFiles: true) { (success, error) in
                if success {
                    completion?(.success(true))
                } else {
                    completion?(.failure(error))
                }
                self.finish()
            }
        } catch {
            completion?(.failure(error))
            self.finish()
        }
    }
}

在控制器中添加一个串行操作队列

let operationQueue : OperationQueue = {
    let queue = OperationQueue()
    queue.name = "FTPQueue"
    queue.maxConcurrentOperationCount = 1
    return queue
}()

并运行操作。如果返回错误,则取消所有挂起的操作

for item in myArray {
    let operation = FTPOperation(item: item)
    operation.completion = { result in
        switch result {
            case .success(_) : print("OK", item.filename)
            case .failure(let error) :
               print(error)
               self.operationQueue.cancelAllOperations()
        }
    }
    operationQueue.addOperation(operation)
}

在方法中加print一行来证明finish()AsynchronousOperation


推荐阅读