我是异步编码的新手,我想知道我用来获取和显示数据的方法在swift中是否正确。
此方法从数据库中的用户部分获取对象列表,然后为列表中的每个项目获取图片。我告诉所有图像是否已被提取的方式是在它们到达时将它们推送到数组,然后如果该数组的长度等于对象列表,则重新加载视图。这是我的代码:
var pets = [String]()
var imgs = [UIImage]()
override func viewDidAppear(_ animated: Bool) {
imgsLoaded = 0
imgs.removeAll(keepingCapacity: false) // Clear images array
pets.removeAll(keepingCapacity: false) // Clear list of objects
let userRef = FIRDatabase.database().reference().child("users").child((FIRAuth.auth()?.currentUser?.uid)!).child("pets")
userRef.observeSingleEvent(of: .value, with: { snapshot in // fetch list of objects from database
if (snapshot.value as? Bool) != nil { // User has no pets added
self.loadScrollView() // Reload view
} else if let snap = snapshot.value as? NSDictionary { // User has pets
for value in snap {
self.pets.append(value.key as! String) // Append object to list of objects
}
for i in 0..<self.pets.count { // For each item in the list, fetch its corresponding image
let imgRef = FIRStorage.storage().reference().child("profile_images").child(self.pets[i]+".png")
imgRef.data(withMaxSize: 15 * 1024 * 1024) { (data, error) -> Void in
if error != nil {
print(error)
}
// Create a UIImage, add it to the array
self.imgs.append(UIImage(data: data!)!) // Push image to list of images
self.imgsLoaded+=1
if self.imgsLoaded == self.pets.count { // If same number of images loaded, reload the view
self.loadScrollView()
}
}
}
}
})
}
正如我所说,我是异步编码的新手,并希望听到我正在尝试的正确方法。我的方法的一个问题是图像阵列可能与数据阵列不对齐,因为图像可以不按顺序取出。我很乐意学习最好的方法,所以请告诉我!
编辑:确保我的数据与相应图像对齐的方法是设置一个字典,其中键是数据,值是我猜的图像。
答案 0 :(得分:2)
我建议使用DispatchGroup
而不是保留2个数组中项目数的计数。这还可以跟踪组中enter()
和leave()
注册的每个块的多个线程的进度。然后,一旦所有块都进入并离开,则调用notify
块,这可以刷新UI。还使用带有占位符图像的字典,以防其中一个图像的加载失败。会比忽视失败案更好。
这也可能更容易阅读和推理,因为没有跟踪2个阵列的数量。 enter()
和leave()
var imgs = [String: UIImage]()
var dispatchGroup = DispatchGroup()
override func viewDidAppear(_ animated: Bool) {
let userRef = FIRDatabase.database().reference().child("users").child((FIRAuth.auth()?.currentUser?.uid)!).child("pets")
userRef.observeSingleEvent(of: .value, with: { [weak self] snapshot in // fetch list of objects from database
if (snapshot.value as? Bool) != nil { // User has no pets added
self?.loadScrollView() // Reload view
} else if let snap = snapshot.value as? NSDictionary { // User has pets
for value in snap {
self?.dispatchGroup.enter()
let imgRef = FIRStorage.storage().reference().child("profile_images").child(self!.pets[i]+".png")
imgRef.data(withMaxSize: 15 * 1024 * 1024) { (data, error) -> Void in
if let error = error {
print(error) // handle the error here but still call .leave() on the group to be sure the notify block is called.
imgs[value] = somePlaceHolderImage
self?.dispatchGroup.leave()
} else if let data = data, let image = UIImage(data: data) {
imgs[value] = image
self?.dispatchGroup.leave()
}
dispatchGroup.notify(queue: DispatchQueue.main, execute: {
self?.loadScrollView()
})
}
}
}
})
}