使用DispatchSemaphore wait()冻结应用程序

时间:2018-08-21 13:14:07

标签: ios swift firebase google-cloud-firestore

我创建了一个getFriends函数,该函数从firestore中读取用户的好友列表,并将每个好友放入LocalUser对象(这是我的自定义用户类)中,以便在表格视图中显示该好友列表。我需要DispatchSemaphore.wait(),因为仅在调用for循环内的完成处理程序时才需要for循环进行迭代。 加载视图时,应用程序冻结。我知道问题是在主线程中调用了semaphore.wait()。但是,通过阅读DispatchQueue-tutorials,我仍然不了解如何解决此问题。 另外:您看到实现我想要做的任何简单的方法了吗?

这是我对viewDidLoad()中的函数的调用:

    self.getFriends() { (friends) in
        self.foundFriends = friends
        self.friendsTable.reloadData()
    }

函数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数组中。

1 个答案:

答案 0 :(得分:1)

几乎永远不要在主线程上有semaphore.wait()。除非您希望等待<10毫秒。

相反,请考虑将您的朋友列表处理分派到后台线程。后台线程可以执行对数据库/ api和wait()的调度,而不会阻塞主线程。

如果您需要触发任何UI工作,只需确保从该线程使用DispatchQueue.main.async {}。

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
    }
}