我已经将此脚本设置为在后台循环访问一堆数据,并且成功设置了一个信号灯以使所有内容(将填充表格的数组)保持秩序,但我无法确切了解其方式或为什么信号量使数组保持顺序。进入dispatchGroup
,循环停止并等待,直到下载图像为止。一旦获得图像,将dispatchSemaphore
设置为1
,立即退出dispatchGroup
并退出信号量设置回0
。信号量从0切换到1太快了,以至于我不明白它如何使数组保持顺序。
let dispatchQueue = DispatchQueue(label: "someTask")
let dispatchGroup = DispatchGroup()
let dispatchSemaphore = DispatchSemaphore(value: 0)
dispatchQueue.async {
for doc in snapshot.documents {
// create data object for array
dispatchGroup.enter()
// get image with asynchronous completion handler
Storage.storage().reference(forURL: imageId).getData(maxSize: 1048576, completion: { (data, error) in
defer {
dispatchSemaphore.signal()
dispatchGroup.leave()
}
if let imageData = data,
error == nil {
// add image to data object
// append to array
}
})
dispatchSemaphore.wait()
}
// do some extra stuff in background after loop is done
}
dispatchGroup.notify(queue: dispatchQueue) {
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
答案 0 :(得分:4)
解决方案在您的评论get image with asynchronous completion handler
中。没有信号灯,所有映像下载将在同一时间开始并竞相完成,因此下载速度最快的映像将首先添加到阵列中。
因此,在开始下载后,您将立即等待信号量。这将阻塞,直到从getData方法在回调关闭中发出信号为止。只有这样循环才能继续下一个文档并下载。这样,您可以一个接一个地下载一个文件,并在下载运行时阻止当前线程。
这里不是使用串行队列的选项,因为这只会导致下载开始连续进行,但是您不能影响下载的完成顺序。
这是一个相当低效的。如果同时给多个请求,则网络层可能会运行得更快(请考虑并行下载和HTTP管道传输)。另外,您正在“浪费”一个线程,该线程可以同时执行一些其他工作。如果同时还有更多工作要做,GCD将产生另一个线程,这将浪费内存和其他资源。
一个更好的模式是跳过信号量,让下载并行运行,然后将图像直接存储在数组中的正确索引处。当然,这意味着您必须事先准备一个适当大小的数组,并且必须考虑一个占位符以防止图像丢失或出现故障。 Optionals可以很好地解决问题:
var images: [UIImage?] = Array(repeating: nil, count: snapshot.documents.count)
for (index, doc) in snapshot.documents.enumerated() {
// create data object for array
dispatchGroup.enter()
// get image with asynchronous completion handler
Storage.storage().reference(forURL: imageId).getData(maxSize: 1048576) { data, error in
defer {
dispatchGroup.leave()
}
if let imageData = data,
error == nil {
// add image to data object
images[index] = image
}
}
}
答案 1 :(得分:0)
DispatchGroup
在这里实际上没有做任何事情。 DispatchSemaphor
授予了互斥,并且该顺序仅由snapshot.documents