提问人:swifter 提问时间:8/27/2021 最后编辑:swifter 更新时间:8/28/2021 访问量:368
带闭合的 For 环
For loop with closure
问:
假设您有一个数组,并且想要遍历数组中的每个元素并调用一个接受该元素作为参数的函数。obj.f
f
是异步的,并且几乎立即完成,但它调用了 中找到的回调处理程序。obj
只有在前一个完成之后才匹配每个元素的最佳方法是什么?
这是一种方式:
let arr = ...
var arrayIndex = 0
var obj: SomeObj! // Required
obj = SomeObj(handler: {
...
arrayIndex += 1
if arrayIndex < arr.count {
obj.f(arr[arrayIndex])
}
})
obj.f(arr[0]) // Assumes array has at least 1 element
这工作正常,但并不理想。
我可以使用一个 ,但这不是很好,因为它会阻塞当前线程。DispatchSemaphore
此外,每个操作必须仅在前一个操作完成时才运行的原因是因为我使用的 api 需要它(或者它中断了)
我想知道是否有更好/更优雅的方法来实现这一目标?
答:
你说:
假设你有一个数组,你想遍历数组中的每个元素并调用一个函数......它接受该元素作为参数。
当一系列异步任务完成时,要知道的基本 GCD 模式是调度组:
let group = DispatchGroup()
for item in array {
group.enter()
someAsynchronousMethod { result in
// do something with `result`
group.leave()
}
}
group.notify(queue: .main) {
// what to do when everything is done
}
// note, don't use the results here, because the above all runs asynchronously;
// return your results in the above `notify` block (e.g. perhaps an escaping closure).
如果你想把它限制在,比如说,最大并发性为 4,你可以使用非零信号量模式(但要确保你不要从主线程这样做),例如
let group = DispatchGroup()
let semaphore = DispatchSemaphore(value: 4)
DispatchQueue.global().async {
for item in array {
group.enter()
semaphore.wait()
someAsynchronousMethod { result in
// do something with `result`
semaphore.signal()
group.leave()
}
}
group.notify(queue: .main) {
// what to do when everything is done
}
}
实现上述目标的等效方法是使用自定义异步子类(使用此处或此处定义的基类),例如Operation
AsynchronousOperation
class BarOperation: AsynchronousOperation {
private var item: Bar
private var completion: ((Baz) -> Void)?
init(item: Bar, completion: @escaping (Baz) -> Void) {
self.item = item
self.completion = completion
}
override func main() {
asynchronousProcess(bar) { baz in
self.completion?(baz)
self.completion = nil
self.finish()
}
}
func asynchronousProcess(_ bar: Bar, completion: @escaping (Baz) -> Void) { ... }
}
然后,您可以执行以下操作:
let queue = OperationQueue()
queue.maxConcurrentOperationCount = 4
let completionOperation = BlockOperation {
// do something with all the results you gathered
}
for item in array {
let operation = BarOperation(item: item) { baz in
// do something with result
}
operation.addDependency(completionOperation)
queue.addOperation(operation)
}
OperationQueue.main.addOperation(completion)
使用非零信号量方法和这种操作队列方法,您可以将并发程度设置为所需的任何程度(例如 1 = 串行)。
但也有其他模式。例如,Combine 也提供了实现这一目标的方法,https://stackoverflow.com/a/66628970/1271826。或者,借助 iOS 15 和 macOS 12 中引入的新 async/await,您可以利用新的协作线程池来限制并发程度。
有很多不同的模式。
评论
您可以尝试使用 swift async/await,如以下示例所示:
struct Xobj {
func f(_ str: String) async {
// something that takes time to complete
Thread.sleep(forTimeInterval: Double.random(in: 1..<3))
}
}
struct ContentView: View {
var obj: Xobj = Xobj()
let arr = ["one", "two", "three", "four", "five"]
var body: some View {
Text("testing")
.task {
await doSequence()
print("--> all done")
}
}
func doSequence() async {
for i in arr.indices { await obj.f(arr[i]); print("--> done \(i)") }
}
}
上一个:Rust 中的闭包数组
评论
AVAssetExportSession