带闭包的 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 需要它(或它中断)
我想知道是否有 better/more 优雅的方法来完成这个?
你说:
Suppose you have an array, and you want to iterate over each element in the array and call a function ... which accepts that element as a parameter.
了解一系列异步任务何时完成的基本 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
subclass(使用基 AsynchronousOperation
class 定义的 or here),例如
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 也提供了实现这一目标的方法 。或者随着 iOS15 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)") }
}
}
假设您有一个数组,您想要遍历数组中的每个元素并调用接受该元素作为参数的函数 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 需要它(或它中断)
我想知道是否有 better/more 优雅的方法来完成这个?
你说:
Suppose you have an array, and you want to iterate over each element in the array and call a function ... which accepts that element as a parameter.
了解一系列异步任务何时完成的基本 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
subclass(使用基 AsynchronousOperation
class 定义的
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 也提供了实现这一目标的方法
有大量不同的模式。
您可以尝试使用 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)") }
}
}