希望你一切顺利。 我正在努力实现以下目标:
目前,我有以下方法
self.dataAccessService.fetchRepliesByCommentId(completionHandler: { (commentReplyArray) in
for var i in 0..<commentReplyArray.count {
let commentReply = commentReplyArray[i]
let commentItem = CommentItem()
self.fetchDetailsAboutCommentReply(commentReplyObject: commentReply) { (commentItem) in
commentItem.commentObject = commentReply
dataSource.insert(commentItem, at: index + i + 1) -> APP CRASHES HERE, i is never 0 here
ips.append(IndexPath(row: index + i + 1 , section: 0))
if (i == commentReplyArray.count - 1) {
self.delegate?.didLoadReplies(dataSource: dataSource, ips: ips)
}
}
}
}, commentId: commentItem.commentObject.id)
我的 fetchDetailsAboutCommentReply 函数:
private func fetchDetailsAboutCommentReply(commentReplyObject:CommentReply, completionHandler:@escaping(CommentItem)->()) {
let group = DispatchGroup()
let commentItem = CommentItem()
group.enter()
self.dataAccessService.fetchUserById(completionHandler: { (userObject) in
commentItem.userObject = userObject
group.leave()
}, uid: commentReplyObject.userId)
group.enter()
self.dataAccessService.fetchDownloadURLOfProfileImage(organizerId: commentReplyObject.userId) { (contentURL) in
commentItem.userObject.contentURL = contentURL
group.leave()
}
group.notify(queue: .main) {
completionHandler(commentItem)
}
}
我的问题是如何更改我的代码,因此循环基本上“暂停”,直到我获取迭代对象的每个详细信息,将其添加到数据源数组中,然后继续下一个?
谢谢并保持健康!
答案 0 :(得分:1)
要具体说明非常困难,因为我们没有关于您的数据源逻辑、类型等的信息。但话说回来,无论如何,我认为我们不想在这里讨论这个问题。
所以,一些一般性观察:
您应该在循环中使用 DispatchGroup
。例如,
let group = DispatchGroup()
for i in ... {
group.enter()
someAsyncMethod { completion in
defer { group.leave() }
...
}
}
group.notify(queue: .main) {
...
}
如您所见,我删除了 if (i == commentReplyArray.count - 1) { ... }
测试,因为您希望这些测试并行运行,并且仅仅因为“最后一个”完成并不意味着它们都已完成。使用 DispatchGroup
及其 notify
方法了解它们何时全部完成。
我对您的 + 1
调用中的 dataSource.insert
逻辑表示怀疑(我们生活在一个基于零索引的世界)。例如。您插入的第一项的索引应该是 0
,而不是 1
。 (如果您正在执行 + 1
逻辑,因为您的 tableview/collection 视图中有一些额外的单元格,我建议不要在此例程中纠缠该偏移索引逻辑,而是让您的“数据源”处理.)
这可能无关紧要,因为无论如何您确实想重构此数据源,因此调用 fetchDetailsAboutComent
完成处理程序的顺序无关紧要。例如,构建一个本地字典,完成后,构建您的排序数组并将其传回:
// dictionary for results, so order doesn't matter
var results: [Int: CommentReply] = [:] // I don't know what the type of your comment/reply is, so adjust this as needed
let group = DispatchGroup()
for i in 0..<20 {
group.enter()
someAsyncMethod { completion in
defer { group.leave() }
...
results[i] = ...
}
}
group.notify(queue: .main) {
// now build array from the dictionary
let array = (0..<20).compactMap { results[i] }
dataSource?.insert(array)
...
}
如果您真的想在结果出现时调用数据源,理论上您可以这样做,但您要确保不仅插入数组,而且 dataSource
对象可以无序处理结果。
您建议您希望循环为一个请求“暂停”,直到前一个请求完成,我强烈建议不要使用这种模式,因为它会使过程变慢(基本上会加剧网络延迟效应)。您确实需要能够让请求并行运行的逻辑。