URLSession 数据任务执行顺序
URLSession dataTask execution order
我正在尝试使用 URLSession dataTask 获取图像数据 url 是从包含每个下载路径的 firebase firestore 文档中获取的,在 snapShotDocuments 中使用 for 循环以升序排列,之后 url 被传递到检索的 URLSession dataTask数据然后将结果附加到数组 tableCells[] 中以更新 tableview,问题是更新的 tableview 中单元格的顺序与 tableCells 数组中对象的顺序不同,我希望它与我在这里不知道的并发是我的代码
public func fetchCells() {
guard (UserDefaults.standard.value(forKeyPath: "email") as? String) != nil else {
return
}
spinner.textLabel.text = "Loading"
spinner.position = .center
spinner.show(in: tableView)
db.collection("ads").order(by: "timeStamp").addSnapshotListener { snapshot, error in
self.tableCells = []
guard error == nil , let snapShotDocuments = snapshot?.documents else {
return
}
guard !snapShotDocuments.isEmpty else {
print("snapshot is empty ")
DispatchQueue.main.async {
self.tableView.isHidden = true
self.spinner.dismiss()
}
return
}
for i in snapShotDocuments {
let documentData = i.data()
guard let imageURL = documentData["imageurl"] as? String , let imageStringURL = URL(string: imageURL) else {
print("no url ")
return
}
guard let descriptionLabel = documentData["adDescription"] as? String , let titleLabel = documentData["adTitle"] as? String , let timeStamp = documentData["timeStamp"] as? Double else {
print("error")
return
}
URLSession.shared.dataTask(with: imageStringURL) { data , _ , error in
guard error == nil , let data = data else {
return
}
let image = UIImage(data: data)
let newCell = adoptionCell(cellImage: image, descriptionLabel: descriptionLabel, titleLabel: titleLabel, timePosted: timeStamp, imageUrl: nil)
self.tableCells.append(newCell)
DispatchQueue.main.async {
self.tableView.reloadData()
self.spinner.dismiss()
}
}.resume()
}
}
}
你有什么:
for i in snapShotDocuments {
dataTask {
mutate tableCells (append) on background thread <- don't do that, A) not thread safe, and B) append won't happen in order they were dispatched, but the order they came back
dispatch back to main {
reload data <- don't do that, reload the individual rows if needed, or reload everything at the end
}
}
您正在排队一些异步操作,这些操作可能需要不同的时间才能完成。例如,按 1、2、3、4 的顺序排列它们,然后它们可以按 3、1、4、2 的顺序返回。
你想要什么:
你的模型,排列好的数据实例,比方说一个结构数组,而不是 UITableViewCell 的。
for i in snapShotDocuments {
dataTask {
process on background thread, but then
dispatch back to main {
look up in the model, the object for which we have the new data
mutate the model array
then reload row at index path for the row involved
}
}
是的正确一些图像可能加载得更快,另一个加载得更慢。因此最终数组中的位置发生了变化。
我宁愿在主线程中访问 tableCells。在这里,我批量重新加载单元格。 index
用于设置单元格在最终数组中的位置。
var tableCells = Array<TableCell?>(repeating: nil, count: snapShotDocuments.count) //preserve space for cells...
var count: Int32 = 0 // actual number of real load tasks
for tuple in snapShotDocuments.enumerated() {
let i = tuple.element
let index = tuple.offset //offset of cell in final array.
let documentData = i.data()
guard let imageURL = documentData["imageurl"] as? String , let imageStringURL = URL(string: imageURL) else {
print("no url ")
return
}
guard let descriptionLabel = documentData["adDescription"] as? String , let titleLabel = documentData["adTitle"] as? String , let timeStamp = documentData["timeStamp"] as? Double else {
print("error")
return
}
count += 1 //increment count as there is new task..
URLSession.shared.dataTask(with: imageStringURL) { data , _ , error in
if error == nil, let data = data {
let image = UIImage(data: data)
let newCell = adoptionCell(cellImage: image, descriptionLabel: descriptionLabel, titleLabel: titleLabel, timePosted: timeStamp, imageUrl: nil)
//self.tableCells.append(newCell)
tableCells[index] = newCell //because array has predefined capacity, thread safe...
}
guard OSAtomicDecrement32(&count) == 0 else { return }
//last task, then batch reload..
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
self.tableCells = tableCells.compactMap { [=10=] }
self.tableView.reloadData()
self.spinner.dismiss()
}
}.resume()
}
我正在尝试使用 URLSession dataTask 获取图像数据 url 是从包含每个下载路径的 firebase firestore 文档中获取的,在 snapShotDocuments 中使用 for 循环以升序排列,之后 url 被传递到检索的 URLSession dataTask数据然后将结果附加到数组 tableCells[] 中以更新 tableview,问题是更新的 tableview 中单元格的顺序与 tableCells 数组中对象的顺序不同,我希望它与我在这里不知道的并发是我的代码
public func fetchCells() {
guard (UserDefaults.standard.value(forKeyPath: "email") as? String) != nil else {
return
}
spinner.textLabel.text = "Loading"
spinner.position = .center
spinner.show(in: tableView)
db.collection("ads").order(by: "timeStamp").addSnapshotListener { snapshot, error in
self.tableCells = []
guard error == nil , let snapShotDocuments = snapshot?.documents else {
return
}
guard !snapShotDocuments.isEmpty else {
print("snapshot is empty ")
DispatchQueue.main.async {
self.tableView.isHidden = true
self.spinner.dismiss()
}
return
}
for i in snapShotDocuments {
let documentData = i.data()
guard let imageURL = documentData["imageurl"] as? String , let imageStringURL = URL(string: imageURL) else {
print("no url ")
return
}
guard let descriptionLabel = documentData["adDescription"] as? String , let titleLabel = documentData["adTitle"] as? String , let timeStamp = documentData["timeStamp"] as? Double else {
print("error")
return
}
URLSession.shared.dataTask(with: imageStringURL) { data , _ , error in
guard error == nil , let data = data else {
return
}
let image = UIImage(data: data)
let newCell = adoptionCell(cellImage: image, descriptionLabel: descriptionLabel, titleLabel: titleLabel, timePosted: timeStamp, imageUrl: nil)
self.tableCells.append(newCell)
DispatchQueue.main.async {
self.tableView.reloadData()
self.spinner.dismiss()
}
}.resume()
}
}
}
你有什么:
for i in snapShotDocuments {
dataTask {
mutate tableCells (append) on background thread <- don't do that, A) not thread safe, and B) append won't happen in order they were dispatched, but the order they came back
dispatch back to main {
reload data <- don't do that, reload the individual rows if needed, or reload everything at the end
}
}
您正在排队一些异步操作,这些操作可能需要不同的时间才能完成。例如,按 1、2、3、4 的顺序排列它们,然后它们可以按 3、1、4、2 的顺序返回。
你想要什么:
你的模型,排列好的数据实例,比方说一个结构数组,而不是 UITableViewCell 的。
for i in snapShotDocuments {
dataTask {
process on background thread, but then
dispatch back to main {
look up in the model, the object for which we have the new data
mutate the model array
then reload row at index path for the row involved
}
}
是的正确一些图像可能加载得更快,另一个加载得更慢。因此最终数组中的位置发生了变化。
我宁愿在主线程中访问 tableCells。在这里,我批量重新加载单元格。 index
用于设置单元格在最终数组中的位置。
var tableCells = Array<TableCell?>(repeating: nil, count: snapShotDocuments.count) //preserve space for cells...
var count: Int32 = 0 // actual number of real load tasks
for tuple in snapShotDocuments.enumerated() {
let i = tuple.element
let index = tuple.offset //offset of cell in final array.
let documentData = i.data()
guard let imageURL = documentData["imageurl"] as? String , let imageStringURL = URL(string: imageURL) else {
print("no url ")
return
}
guard let descriptionLabel = documentData["adDescription"] as? String , let titleLabel = documentData["adTitle"] as? String , let timeStamp = documentData["timeStamp"] as? Double else {
print("error")
return
}
count += 1 //increment count as there is new task..
URLSession.shared.dataTask(with: imageStringURL) { data , _ , error in
if error == nil, let data = data {
let image = UIImage(data: data)
let newCell = adoptionCell(cellImage: image, descriptionLabel: descriptionLabel, titleLabel: titleLabel, timePosted: timeStamp, imageUrl: nil)
//self.tableCells.append(newCell)
tableCells[index] = newCell //because array has predefined capacity, thread safe...
}
guard OSAtomicDecrement32(&count) == 0 else { return }
//last task, then batch reload..
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
self.tableCells = tableCells.compactMap { [=10=] }
self.tableView.reloadData()
self.spinner.dismiss()
}
}.resume()
}