如何调用组合多个完成处理程序以将数据组合到一个新数组

Posted

技术标签:

【中文标题】如何调用组合多个完成处理程序以将数据组合到一个新数组【英文标题】:How to call combine multiple completion handlers to combine data to one new array 【发布时间】:2019-06-15 01:44:54 【问题描述】:

我已经被困了一段时间,任何建议都将不胜感激。我正在创建一个使用 Firebase 数据库的应用程序,并且我创建了 5 个在 Firebase 中保存不同数据的类。我正在创建一个表格视图,它需要显示来自 5 个类中的每一个的信息(个人资料名称、图像、然后是关于联赛的信息和关于分数的信息)。因此,在我的新课程中,我创建了一个函数,从每个课程中调用来自 firebase 的数据...... 例如:获取X联赛的所有球员 对于联盟中的每一位球员 获取玩家信息 然后得到分数 然后继续 一旦我们将所有信息追加到新数组 然后对数组进行排名

在所有这些运行之后,我想在 VC 上重新加载表视图

所以我的解决方案适用于原始负载,但如果我退出并重新进入屏幕,名称和图像会重复。

确切地说,当索引打印到我得到的控制台时 “玩家 1:扎克” “玩家 2:约翰” 然而,屏幕反复显示约翰的形象和名字。但只有那个类......所有其他数据都留在它应该在的地方。并且原来的函数都是用同样的方式写的。

我认为这与内存管理有关,或者我的完成处理程序写得不好?

这是新数组类中的代码:

您还会注意到我的完成()在我讨厌的 for in 循环中,但这是我可以在完成之前完成函数的唯一方法。否则函数在数据准备好之前完成。

func getLeaderboard(leagueID: String, completion: @escaping ()->()) 

        print("League Count After removeAll \(self.rankedGolfers.count)")
        self.leagueMembers.getLeagueMembers(leagueID: leagueID) 
            print("HANDLER: Step 1: Get League Members")

            for member in self.leagueMembers.leagueMembers 

                print("Golfer Member ID: \(member.userID)")
                self.golferInfo.getGolferInfo(userKey: member.userID, completion: 
                    print("HANDLER: Step 2: Get player profile info")
                    print("Golfer Name3: \(self.golferInfo.golfers[0].firstName) \(self.golferInfo.golfers[0].lastName)")
                    self.handicapHelper.getRounds(userID: member.userID, completion: 
                        print("HANDLER: Step 3: Get players Handicap")
                        print("Golfer Handicap3: \(self.golferInfo.golfers[0].lastName): \(self.handicapHelper.handicap)")

                        self.leagueInfo.getLeagueInfo(leagueID: leagueID, completion: 
                            print("HANDLER: Step 4: Get league info")
                            let golferIndex = self.golferInfo.golfers[0]
                            let memberInfoIndex = self.leagueInfo.leagueInfo[0]
                            let golferID = member.userID
                            let profileImg = golferIndex.profileImage
                            let golferName = "\(golferIndex.firstName) \(golferIndex.lastName)"
                            let handicap = self.handicapHelper.handicap
                            let golferLeaguePardieScore = member.pardieScore
                            let leagueRoundsPlayed = member.numberOfRounds
                            let roundsRemaining = memberInfoIndex.leagueMinRounds - leagueRoundsPlayed
                            let currentWinnings = member.currentWinnings

                            let newGolfer = Leaderboard(golferID: golferID, profileImg: profileImg ?? "No Img", golferName: golferName, golferHandicap: handicap, golferLeaguePardieScore: golferLeaguePardieScore, roundsPlayedInLeague: leagueRoundsPlayed, roundsRemaining: roundsRemaining, currentWinnings: currentWinnings)

                            self.rankedGolfers.append(newGolfer)

                            print("HANDLER: Step 5: Add golfer to array")
                            //print("Golfer Name 4: \(newGolfer.golferName)")
                            //print("Rounds Remaining: \(newGolfer.roundsRemaining)")
                            print("league Member Count: \(self.rankedGolfers.count)")
                            self.getLeaderboardRanking()
                            print("HANDLER: Step 6: Rank Array")
                            //print("COMPLETION: \(self.rankedGolfers.count)")
                            completion()
                        )
                    )

                )
            
        


感谢您提供的任何帮助!

【问题讨论】:

为什么快速枚举中有'completion()'? 【参考方案1】:

我认为我们可以使用 DispatchGroup 解决这个问题,这将确保为每个用户加载所有数据,然后将用户附加到用作 tableView 数据源的数组中,然后在完成后重新加载 tableView。

为简单起见,我们将从一个 UserInfo 类开始,该类存储他们的 uid、姓名、最喜欢的食物和障碍。

class UserInfoClass 
    var uid = ""
    var name = ""
    var favFood = ""
    var handicap = 0

还有一个类 var 数组用作 tableView 的数据源

var userInfoArray = [UserInfoClass]()

那么,假设我们有这样的结构......

users
   uid_0
      name: "Leroy"

handicaps
   uid_0
      amt: 4

fav_foods
   uid_0
      fav_food: "Pizza"

...这是一个读取所有用户的函数,然后遍历每个用户,用他们的名字和 uid 填充 UserInfoClass,并创建一个调度组,该组也填充他们最喜欢的食物和障碍。完成后,用户被添加到 dataSource 数组中,当所有用户都被读取时,tableView 被重新加载以显示信息。

func loadUsersInfoAndHandicap() 
    let ref = self.ref.child("users")
    self.userInfoArray = []
    ref.observeSingleEvent(of: .value, with:  snapshot in
        let group = DispatchGroup()
        let allUsers = snapshot.children.allObjects as! [DataSnapshot]
        for user in allUsers 
            let uid = user.key
            let name = user.childSnapshot(forPath: "name").value as? String ?? "No Name"

            let aUser = UserInfoClass()
            aUser.uid = uid
            aUser.name = name

            group.enter()
            self.loadFavFood(withUid: uid) favFood in
                aUser.favFood = favFood
                group.leave()
            

            group.enter()
            self.loadHandicap(withUid: uid)  handicap in
                aUser.handicap = handicap
                group.leave()
            

            group.notify(queue: .main) 
                self.userInfoArray.append(aUser)
            
        

        group.notify(queue: .main) 
            print("done, reload the tableview")
            for user in self.userInfoArray 
                print(user.uid, user.name, user.favFood, user.handicap)
            
        
    )

用户名和 uid 是从主用户节点读取的,这是读取他们最喜欢的食物和障碍的两个函数。

func loadFavFood(withUid: String, completion: @escaping(String) -> Void) 
    let thisUser = self.ref.child("userInfo").child(withUid)
    thisUser.observeSingleEvent(of: .value, with:  snapshot in
        let food = snapshot.childSnapshot(forPath: "fav_food").value as? String ?? "No Fav Food"
        completion(food)
    )


func loadHandicap(withUid: String, completion: @escaping(Int) -> Void) 
    let thisUser = self.ref.child("handicaps").child(withUid)
    thisUser.observeSingleEvent(of: .value, with:  snapshot in
        let handicap = snapshot.childSnapshot(forPath: "amt").value as? Int ?? 0
        completion(handicap)
    )

请注意 self.ref 指向我的 firebase,因此请替换对 your firebase 的引用。

注意,我输入得很快,基本上没有错误检查,所以请相应地添加。

【讨论】:

嗨杰,非常感谢您的帮助..但是我仍然遇到同样的问题。当我第一次加载时,一切都正确加载。然后,如果我退出并返回数据,数据会在人身上重复。这是控制台。它的打印方式是它在表格视图中的显示方式。高尔夫球手姓名:Zack Blauvelt 高尔夫球手让分 13.1 高尔夫球手姓名:John Smith 高尔夫球手让分 15.8 高尔夫球手姓名:John Smith 高尔夫球手让分 15.8 高尔夫球手姓名:John Smith 高尔夫球手让分 15.8 @ZackBlauvelt 如果您多次运行上述代码,它将加载tableView dataSource 数组,其中包含份数据。例如,如果您有 5 个用户,运行代码,数组将有 5 个用户。如果您在不退出应用程序的情况下再次运行它,它将有 10 个用户。当使用self.userInfoArray = [] 调用此代码时,您可以通过重置数组来更改该行为。它出现在代码中函数名之后。

以上是关于如何调用组合多个完成处理程序以将数据组合到一个新数组的主要内容,如果未能解决你的问题,请参考以下文章

将多个 API 调用组合到一个请求中

WPF DataGridTemplateColumn组合框问题

组合选项框和组合框以将记录添加到右表

如何在组合中安排异步调用的同步序列?

如何在 Flux 中处理具有依赖关系的数据组合和检索?

Apache Ignite中大量相似性调用的有效组合