代码之家  ›  专栏  ›  技术社区  ›  Ron Baker

正确放置dispatchGroup以重新加载数据

  •  1
  • Ron Baker  · 技术社区  · 6 年前

    我有一个tableview函数,它从数据库中提取数据来渲染单元格。我想实现这样一个目标,即不要过多地重新加载我的tableview。我了解到,调度组将是一种可行的方法,因为我不想返回到完成块,在提取所有数据之前重新加载tableView。然而,当我使用调度组时,它永远不会到达完成位置,它只会停止。我的变量可能放错了地方,但我真的看不出应该放在哪里。我一直在把它搬到不同的地方,但还是一无所获。

    import UIKit
    import Firebase
    
    class FriendsEventsView: UITableViewController{
        var cellID = "cellID"
        var friends = [Friend]()
        var attendingEvents = [Event]()
        //label that will be displayed if there are no events
        var currentUserName: String?
        var currentUserPic: String?
        var currentEventKey: String?
    
        override func viewDidLoad() {
            super.viewDidLoad()
            self.title = "Friends Events"
            view.backgroundColor = .white
            // Auto resizing the height of the cell
            tableView.estimatedRowHeight = 44.0
            tableView.rowHeight = UITableViewAutomaticDimension
            self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "close_black").withRenderingMode(.alwaysOriginal), style: .done, target: self, action: #selector(self.goBack))
            tableView.register(EventDetailsCell.self, forCellReuseIdentifier: cellID)
            self.tableView.tableFooterView = UIView(frame: CGRect.zero)
    
    
    
        }
        override func viewWillAppear(_ animated: Bool) {
            super.viewWillAppear(animated)
            DispatchQueue.global(qos: .background).async {
                print("This is run on the background queue")
                self.fetchEventsFromServer { (error) in
                    if error != nil {
                        print(error)
                        return
                    } else {
                        DispatchQueue.main.async {
                            self.tableView.reloadData()
                            print("This is run on the main queue, after the previous code in outer block")
                        }
    
                    }
                }
    
    
            }
    
        }
    
        @objc func goBack(){
            dismiss(animated: true)
        }
        override func numberOfSections(in tableView: UITableView) -> Int {
           // print(friends.count)
            return friends.count
        }
        override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
           // print(friends[section].events.count)
            return friends[section].collapsed ? 0 : friends[section].events.count
        }
        func tableView(_ tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
            return UITableViewAutomaticDimension
        }
        override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: cellID) as! EventDetailsCell? ?? EventDetailsCell(style: .default, reuseIdentifier: cellID)
           // print(indexPath.row)
            cell.details = friends[indexPath.section].events[indexPath.row]
            return cell
        }
    
        override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
            let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: "header") as? CollapsibleTableViewHeader ?? CollapsibleTableViewHeader(reuseIdentifier: "header")
           // print(section)
            header.arrowLabel.text = ">"
            header.setCollapsed(friends[section].collapsed)
            print(friends[section].collapsed)
            header.section = section
           // header.delegate = self
            header.friendDetails = friends[section]
            return header
        }
        override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
            return 50
        }
       func fetchEventsFromServer(_ completion: @escaping (_ error: Error?) -> Void ){
            //will grab the uid of the current user
    
            guard let myUserId = Auth.auth().currentUser?.uid else {
                return
            }
            let ref = Database.database().reference()
            //checking database for users that the current user is following
            ref.child("following").child(myUserId).observeSingleEvent(of: .value, with: { (followingSnapshot) in
               //handling potentail nil or error cases
                guard let following = followingSnapshot.children.allObjects as? [DataSnapshot]
                    else {return}
    
                //validating if proper data was pulled
                let group = DispatchGroup()
    
                for followingId in following {
                    group.enter()
                    UserService.show(forUID: followingId.key, completion: { (user) in
                        PostService.showFollowingEvent(for: followingId.key, completion: { (event) in
                            self.attendingEvents = event
                            var friend = Friend(friendName: (user?.username)!, events: self.attendingEvents, imageUrl: (user?.profilePic)!)
                            self.friends.append(friend)
                        })
                    })
                }
    

    此循环应返回到VIEWWILLEXPEND中的completon块,并在执行此if语句后显示

                if self.friends.count == following.count{
                    group.leave()
                    let result = group.wait(timeout: .now() + 0.01)
                    //will return this when done
                    completion(nil)
                }
    
            }) { (err) in
                completion(err)
                print("Couldn't grab people that you are currently following: \(err)")
            }
    
        }
    

    非常感谢您的帮助

    1 回复  |  直到 6 年前
        1
  •  2
  •   Milan Nosáľ    6 年前

    您要放置 group.leave() 的内部 PostService.showFollowingEvent 回调。

    现在你打电话 enter following.count -次,但你打电话 leave 只有一次。要继续该组,您必须在进入该组时多次离开该组:

            for followingId in following {
                group.enter()
                UserService.show(forUID: followingId.key, completion: { (user) in
                    PostService.showFollowingEvent(for: followingId.key, completion: { (event) in
                        self.attendingEvents = event
                        var friend = Friend(friendName: (user?.username)!, events: self.attendingEvents, imageUrl: (user?.profilePic)!)
                        self.friends.append(friend)
                        // leave here
                        group.leave()
                    })
                })
            }
    

    此外,我不建议使用 group.wait 因为你可能面临僵局。如果任何应该调用的回调 group.leave 发生在与 组等待 被调用后,他们将永远不会被调用,而您将最终得到冻结的线程。相反,使用 group.notify :

        group.notify(queue: DispatchQueue.main) {
            if self.friends.count == following.count {
                completion(nil)
            }
        }
    

    这将允许在主线程上执行,但一旦所有任务完成,它将执行提供的回调闭包。