从 Firebase 检索数据以在 table 视图中显示数据时出现并发问题

Concurrency problem when retrieving data from Firebase to display it in a table view

我目前正在尝试从 Swift 中的实时数据库 (Firebase) 检索用户数据,并在 table 视图中显示用户名。 我有以下函数来从位于我的文件的主 class 中的数据库中检索所有用户(我附加到的变量用户已经作为空字符串数组在早些时候启动)。

func retrieveAllUsers(){

        //hold all users
        var userFullName = ""

        self.usersReference.observeSingleEvent(of: .value, with: {(snapshot) in
        // if marker ID has children it means this marker has already been created
            //go in User ID uid
            if snapshot.hasChildren() {
                // go through all user ids
                for child in snapshot.children {
                    //initialize a user name and first name
                    var userFirstName = ""
                    var userLastName = ""
                    let subSnap = child as! DataSnapshot
                    //get child key with format UserID: uid
                    let key = subSnap.key
                    // go inside User ID to retrieve name and first name of every user
                    DispatchQueue.main.async {
                        self.usersReference.child(key).observeSingleEvent(of: .value, with: { (snapshot2) in
                            // go through user parammters to only retrieve his name and first name
                            for grandChild in snapshot2.children {
                                let nameGatherer = grandChild as! DataSnapshot
                                if nameGatherer.key == "First Name:"{
                                    userFirstName = nameGatherer.value as! String
                                }
                                if nameGatherer.key == "Last Name:"{
                                    userLastName = nameGatherer.value as! String
                                }
                            }//end of loop inside user parameters
                            //add his full name to the list of users which we will then filter later on
                            userFullName = userFirstName + userLastName
                            self.users.append(userFullName)
                            self.tableView.reloadData()
                            print("IN CLOSURE: \(self.users)")
                        })// end of observation of one user parameters
                    }//end of dispatch async
                print("OUTSIDE CLOSURE1: \(self.users)")
                }//end of looking inside the user ID node
            print("OUTSIDE CLOSURE2: \(self.users)")
            }//end of if there are users children
        print("OUTSIDE CLOSURE3: \(self.users)")
        })//end of observation of users reference
        print("OUTSIDE CLOSURE4: \(self.users)")
    }

在我的 viewDidLoad 中我有

retrieveAllUsers()
print("USERS:\(self.users)")
tableView.reloadData()

我已经尝试使用 Dispatchmain.async 的并发(这是我第一次在我的程序中进行并发)。然而它似乎不起作用,因为我的 viewDidLoad 中的打印首先出现并且用户为空因此它没有出现在我的 table 视图中并且打印 ** print("OUTSIDE CLOSURE1: (self.users)")** 最后出现使用包含正确用户信息的数组。

谁能帮我在 table 视图中显示正确的用户信息?我真的很感激。 非常感谢! 安东尼

您的问题是因为您对 self.tableView.reloadData() 的调用实际上是在包含您的数据的 Firebase 回调中。此回调不保证在主线程上,所有UI操作必须在主线程上发生,总是。您需要包装调用以直接在 DispatchQueue.main.async 中重新加载 table(不嵌套在另一个调用中)以确保在主线程上调用它。

您也不需要在DispatchQueue.main.async中调用嵌套调用。看我的例子:

func retrieveAllUsers(){

        //hold all users
        var userFullName = ""

        self.usersReference.observeSingleEvent(of: .value, with: {(snapshot) in
        // if marker ID has children it means this marker has already been created
            //go in User ID uid
            if snapshot.hasChildren() {
                // go through all user ids
                for child in snapshot.children {
                    //initialize a user name and first name
                    var userFirstName = ""
                    var userLastName = ""
                    let subSnap = child as! DataSnapshot
                    //get child key with format UserID: uid
                    let key = subSnap.key
                    // go inside User ID to retrieve name and first name of every user

                        self.usersReference.child(key).observeSingleEvent(of: .value, with: { (snapshot2) in
                            // go through user parammters to only retrieve his name and first name
                            for grandChild in snapshot2.children {
                                let nameGatherer = grandChild as! DataSnapshot
                                if nameGatherer.key == "First Name:"{
                                    userFirstName = nameGatherer.value as! String
                                }
                                if nameGatherer.key == "Last Name:"{
                                    userLastName = nameGatherer.value as! String
                                }
                            }//end of loop inside user parameters
                            //add his full name to the list of users which we will then filter later on
                            userFullName = userFirstName + userLastName
                            self.users.append(userFullName)
                            DispatchQueue.main.async {
                                self.tableView.reloadData()
                            }
                            print("IN CLOSURE: \(self.users)")
                        })// end of observation of one user parameters

                print("OUTSIDE CLOSURE1: \(self.users)")
                }//end of looking inside the user ID node
            print("OUTSIDE CLOSURE2: \(self.users)")
            }//end of if there are users children
        print("OUTSIDE CLOSURE3: \(self.users)")
        })//end of observation of users reference
        print("OUTSIDE CLOSURE4: \(self.users)")
    }