我的应用(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!")
}
}
添加break
会给我一条错误消息:
未标记的“中断”仅允许在循环或开关(带标记的 要退出if或do,必须先休息一下
向循环(myLoop: for (i,s) in myArray.enumerated()
)添加标签也不起作用:
使用未解决的标签“ myLoop”
break self.myLoop
也失败了。
在print
之前添加group.enter()
可以证明循环并不仅仅是在完成第一个文件的上传之前完成,而是在“成功上传” /“成功上传”之前打印了文本失败”(按理应如此)。由于这种破坏应该是可能的:
如何中断循环,以便可以从group.notify
中显示错误对话框?
答案 0 :(得分:0)
不使用递归的简单解决方案:添加Bool
来检查循环是否应该中断,然后在完成处理程序之外中断该循环:
let group = DispatchGroup()
let queue = DispatchQueue(label: "someLabel")
let sema = DispatchSemaphore(value: 0)
queue.async {
var everythingOkay:Bool = true
for (i,item) in myArray.enumerated() {
//print("Loop iteration: \(i)")
if everythingOkay {
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!")
everythingOkay = true
} else {
print("Upload failed!")
everythingOkay = false
}
group.leave()
sema.signal()
})
sema.wait()
} catch {
print("Error: \(error.localizedDescription)")
everythingOkay = false
}
} else {
break
}
}
}
group.notify(queue: queue) {
DispatchQueue.main.async {
print("Done!")
}
}
通常不能像这样使用Bool
,因为循环将在第一个文件上传之前完成。
这是DispatchGroup
和DispatchSemaphore
发挥作用的地方:它们确保下一个循环迭代直到上一个循环完成之前才开始,这意味着文件将被上传到它们在myArray
中列出的顺序(建议使用这种方法here)。
可以使用上面代码中的打印内容进行测试,然后将在“成功上传!” /“成功上传!”之前进行打印。对于每次迭代,例如:
Loop iteration: 0
Upload successful
Loop iteration: 1
Upload successful
Loop iteration: 2
Upload failed
Done!
答案 1 :(得分:0)
我建议的方法基于this question的可接受答案中提供的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
的{{1}}方法中添加finish()
行以证明这一点