为啥这个完成处理程序会跳过一个 for 循环
Posted
技术标签:
【中文标题】为啥这个完成处理程序会跳过一个 for 循环【英文标题】:Why is this completion handler skipping a for loop为什么这个完成处理程序会跳过一个 for 循环 【发布时间】:2020-01-20 10:16:52 【问题描述】:我有这组代码,我只想运行:self.performSegue
在所有 for 循环和 Firebase 的所有异步任务都运行完之后:
getFeaturedPost(withCompletion: startNext)
func getFeaturedPost(withCompletion completion: () -> Void )
print("Getting featured posts...")
ref.child("featured").child("amount").observeSingleEvent(of: .value, with: (snapshot) in
self.numberOfPosts = snapshot.value as! Int
print("There's \(self.numberOfPosts) posts avaliable.")
for pos in 0..<self.numberOfPosts
print("Getting reference names for post: \(pos + 1)")
self.ref.child("featured").child("post\(pos + 1)").observeSingleEvent(of: .value, with: (snapshot) in
let postID = (snapshot.value as? NSDictionary)?["postID"] as? String ?? ""
let userOfPost = (snapshot.value as? NSDictionary)?["userOfPost"] as? String ?? ""
self.customValues.append(("/users/public/\(userOfPost)/posts/\(postID)"))
)
)
print("Done! The posts are: \(customValues)")
completion()
func startNext()
getPostData(withCompletion:
print("Finished getting data, going to main screen.")
self.performSegue(withIdentifier: "showHome", sender: nil)
)
func getPostData(withCompletion completion: () -> Void )
print("Getting idividual post data, there are \(customValues.count) posts")
for i in 0..<customValues.count
print("Currently on post: \(i)")
let encodedURL = (customValues[i] + "/postURL")
ref.child(encodedURL).observe(.value, with: (snapshot) in
if let newURL = snapshot.value as? String
print("Sending \(newURL) to DemoSource Class")
DemoSource.shared.add(urlString: newURL)
)
completion()
然而startNext()
函数(转到下一个视图)在getFeaturedPost
开始之前执行,它是for 循环,它打印它当前所在的帖子。最后,当我使用DemoSource.shared.add(urlString: newURL)
将数据发送到演示源类时,newURL
为 nil,我有一个控制台日志,显示每个函数的打印语句的顺序:
Getting featured posts...
Done! The posts are: []
Getting idividual post data, there are 0 posts
Finished getting data, going to main screen. // This line should be printed last meaning this function is being executed too early
There's 2 posts avaliable.
Getting reference names for post: 1 // These two lines should be called before the line 'Finished getting data'
Getting reference names for post: 2
【问题讨论】:
print(Done
行和completion()
调用立即执行。数据库请求很晚才返回数据。在循环中使用异步任务,您需要DispatchGroup
@vadian 我想了很多,你能指出我应该在哪里添加 dispatchGroups,因为我知道在我做两个 firebase 查询时可能不止一个地方可以添加它们。
【参考方案1】:
DispatchGroup
的使用非常简单。这是一种计数器。 enter
增加计数器,leave
减少它。如果计数器达到0
,则执行notify
中的闭包。
enter
之前的循环中。
在异步块内部结束调用leave
。
在循环调用notify
之后。
func getFeaturedPost(withCompletion completion: @escaping () -> Void )
print("Getting featured posts...")
ref.child("featured").child("amount").observeSingleEvent(of: .value, with: (snapshot) in
self.numberOfPosts = snapshot.value as! Int
print("There's \(self.numberOfPosts) posts avaliable.")
let group = DispatchGroup()
for pos in 0..<self.numberOfPosts
group.enter()
print("Getting reference names for post: \(pos + 1)")
self.ref.child("featured").child("post\(pos + 1)").observeSingleEvent(of: .value, with: (snapshot) in
if let post = snapshot.value as? [String:Any]
let postID = post["postID"] as? String ?? ""
let userOfPost = post["userOfPost"] as? String ?? ""
self.customValues.append(("/users/public/\(userOfPost)/posts/\(postID)"))
group.leave()
)
group.notify(queue: .main)
print("Done! The posts are: \(customValues)")
completion()
)
在其他方法中相应地实现一个组。
旁注:不要在 Swift 中使用 NS...
集合类型。
【讨论】:
很好的答案先生 :) @jawadAli 如果答案很好,请点赞 :) 评论用于要求澄清或指出帖子中的问题。 我使用时出现两个错误Escaping closure captures non-escaping parameter 'completion'
我需要@escaping吗?【参考方案2】:
调度组
组允许您聚合一组任务并同步行为 在群里。您将多个工作项附加到一个组并安排 它们用于在同一队列或不同队列上异步执行。 当所有工作项完成执行时,组执行其 完成处理程序。您还可以同步等待中的所有任务 完成执行的组。 Apple Documentation
您可以使用 DispatchGroup 编辑您的方法
func getFeaturedPost(withCompletion completion: @escaping() -> Void )
let group = DispatchGroup() // create group
print("Getting featured posts...")
group.enter() // enter group
ref.child("featured").child("amount").observeSingleEvent(of: .value, with: (snapshot) in
self.numberOfPosts = snapshot.value as! Int
print("There's \(self.numberOfPosts) posts avaliable.")
for pos in 0..<self.numberOfPosts
group.enter() // enter group
print("Getting reference names for post: \(pos + 1)")
self.ref.child("featured").child("post\(pos + 1)").observeSingleEvent(of: .value, with: (snapshot) in
let postID = (snapshot.value as? NSDictionary)?["postID"] as? String ?? ""
let userOfPost = (snapshot.value as? NSDictionary)?["userOfPost"] as? String ?? ""
self.customValues.append(("/users/public/\(userOfPost)/posts/\(postID)"))
group.leave()
)
group.leave() //
)
print("Done! The posts are: \(customValues)")
group.notify(queue: .main, execute: // executed after all async calls in for loop finish
print("done with all async calls")
// do stuff
completion()
)
func getPostData(withCompletion completion:@escaping () -> Void )
print("Getting idividual post data, there are \(customValues.count) posts")
let group = DispatchGroup() // create group
for i in 0..<customValues.count
group.enter()
print("Currently on post: \(i)")
let encodedURL = (customValues[i] + "/postURL")
ref.child(encodedURL).observe(.value, with: (snapshot) in
if let newURL = snapshot.value as? String
print("Sending \(newURL) to DemoSource Class")
DemoSource.shared.add(urlString: newURL)
group.leave()
)
group.notify(queue: .main, execute: // executed after all async calls in for loop finish
completion()
)
【讨论】:
以上是关于为啥这个完成处理程序会跳过一个 for 循环的主要内容,如果未能解决你的问题,请参考以下文章