问题描述
我创建了一个getFriends函数,该函数从firestore中读取用户的好友列表,并将每个好友放入LocalUser对象(这是我的自定义用户类)中,以便在表格视图中显示该好友列表.我需要DispatchSemaphore.wait(),因为仅在调用for循环内的完成处理程序时才需要for循环进行迭代.加载视图时,该应用程序冻结.我知道问题是在主线程中调用了semaphore.wait().但是,通过阅读DispatchQueue-tutorials,我仍然不了解如何解决此问题.另外:您看到实现我想要做的任何更简单的方法了吗?
I have created a function getFriends that reads a User's friendlist from firestore and puts each friend in a LocalUser object (which is my custom user class) in order to display the friendlist in a tableview. I need the DispatchSemaphore.wait() because I need the for loop to iterate only when the completion handler inside the for loop is called.When loading the view, the app freezes. I know that the problem is that semaphore.wait() is called in the main thread. However, from reading DispatchQueue-tutorials I still don't understand how to fix this in my case.Also: do you see any easier ways to implement what I want to do?
这是我对viewDidLoad()中的函数的调用:
This is my call to the function in viewDidLoad():
self.getFriends() { (friends) in
self.foundFriends = friends
self.friendsTable.reloadData()
}
函数getFriends:
And the function getFriends:
let semaphore = DispatchSemaphore(value: 0)
func getFriends(completion: @escaping ([LocalUser]) -> ()) {
var friendsUID = [String : Any]()
database.collection("friends").document(self.uid).getDocument { (docSnapshot, error) in
if error != nil {
print("Error:", error!)
return
}
friendsUID = (docSnapshot?.data())!
var friends = [LocalUser]()
let friendsIdents = Array(friendsUID.keys)
for (idx,userID) in friendsIdents.enumerated() {
self.getUser(withUID: userID, completion: { (usr) in
var tempUser: LocalUser
tempUser = usr
friends.append(tempUser)
self.semaphore.signal()
})
if idx == friendsIdents.endIndex-1 {
print("friends at for loop completion:", friends.count)
completion(friends)
}
self.semaphore.wait()
}
}
}
friendsUID是一个字典,每个朋友的uid为键,而true为值.由于我只需要密钥,因此将它们存储在数组friendsIdents中.函数getUser在firestore中搜索传递的uid,并创建相应的LocalUser(usr).最终将其添加到friends数组中.
friendsUID is a dict with each friend's uid as a key and true as the value. Since I only need the keys, I store them in the array friendsIdents. Function getUser searches the passed uid in firestore and creates the corresponding LocalUser (usr). This finally gets appended in friends array.
推荐答案
几乎永远不要在主线程上有semaphore.wait().除非您期望等待<10毫秒.
You should almost never have a semaphore.wait() on the main thread. Unless you expect to wait for < 10ms.
相反,请考虑将您的朋友列表处理分派到后台线程.后台线程可以执行对数据库/api和wait()的调度,而不会阻塞主线程.
Instead, consider dispatching your friends list processing to a background thread. The background thread can perform the dispatch to your database/api and wait() without blocking the main thread.
如果您需要触发任何UI工作,只需确保从该线程使用DispatchQueue.main.async {}.
Just make sure to use DispatchQueue.main.async {} from that thread if you need to trigger any UI work.
let semaphore = DispatchSemaphore(value: 0)
func getFriends(completion: @escaping ([LocalUser]) -> ()) {
var friendsUID = [String : Any]()
database.collection("friends").document(self.uid).getDocument { (docSnapshot, error) in
if error != nil {
print("Error:", error!)
return
}
DispatchQueue.global(qos: .userInitiated).async {
friendsUID = (docSnapshot?.data())!
var friends = [LocalUser]()
let friendsIdents = Array(friendsUID.keys)
for (idx,userID) in friendsIdents.enumerated() {
self.getUser(withUID: userID, completion: { (usr) in
var tempUser: LocalUser
tempUser = usr
friends.append(tempUser)
self.semaphore.signal()
})
if idx == friendsIdents.endIndex-1 {
print("friends at for loop completion:", friends.count)
completion(friends)
}
self.semaphore.wait()
}
// Insert here a DispatchQueue.main.async {} if you need something to happen
// on the main queue after you are done processing all entries
}
}
这篇关于使用DispatchSemaphore wait()冻结应用程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!