UITableView重复调用reloadData致能耗过高问题求助
解决UITableView无限重载与卡顿问题的方案
兄弟,你这问题的根源太明显了——你在cellForRowAt里调用reloadData(),直接导致了无限循环:每次tableView刷新都会重新创建所有cell,每个cell创建时又触发刷新,循环往复,不卡顿、不耗电才怪!
咱们换个思路,正确的做法是只在数据准备完成后调用一次刷新,而不是在cell创建时瞎折腾。下面一步步给你改:
1. 先砍掉cell里的致命代码
把cellForRowAt里的DispatchQueue.main.async { reloadData() }删掉,这是无限循环的罪魁祸首。修改后的cell方法应该是这样的:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { // 推荐提前注册cell,这里先兼容你的写法 var cell = charitiesSupportedTable.dequeueReusableCell(withIdentifier: "SupportedCell") if cell == nil { cell = UITableViewCell(style: .subtitle, reuseIdentifier: "abc") } // 因为numberOfRows会返回数组长度,所以不用判断越界 let charityName = charOfPrevPostsArray[indexPath.row] let moneyRaised = moneyRaisedOfPrevPostsArray[indexPath.row] cell?.textLabel?.text = charityName cell?.detailTextLabel?.text = moneyRaised return cell! }
2. 在数据加载完成后才刷新tableView
你的Firebase数据加载是异步的,之前数据回来后你只更新了数组,但没通知tableView刷新,所以一开始cell里没内容。现在要在数据处理完后,在主线程触发刷新:
override func viewDidLoad() { super.viewDidLoad() guard let userEmail = Auth.auth().currentUser?.email else { // 处理用户未登录的情况,避免崩溃 return } var email1 = userEmail.replacingOccurrences(of: "@", with: "", options: .literal) email1 = email1.replacingOccurrences(of: ".", with: "", options: .literal) charitiesSupportedTable.delegate = self charitiesSupportedTable.dataSource = self charitiesSupportedTable.isScrollEnabled = false // 加载用户过往帖子数据 let postsRef = Database.database().reference().child("Posts") let userPostsRef = postsRef.queryOrdered(byChild: "user").queryEqual(toValue: email1) userPostsRef.observe(.value, with: { [weak self] snapshot in guard let self = self else { return } // 每次数据更新先清空数组,防止重复添加 self.charOfPrevPostsArray.removeAll() self.moneyRaisedOfPrevPostsArray.removeAll() for child in snapshot.children { let snap = child as! DataSnapshot guard let valueAsDict = snap.value as? [String: Any], let charity = valueAsDict["charity"] as? String, let viewCountStr = valueAsDict["viewCount"] as? String, let viewCount = Double(viewCountStr) else { // 跳过格式错误的数据,避免崩溃 continue } let moneyRaised = viewCount * 0.006 self.charOfPrevPostsArray.append(charity) self.moneyRaisedOfPrevPostsArray.append(String(moneyRaised)) } // 反转数组 self.charOfPrevPostsArray.reverse() self.moneyRaisedOfPrevPostsArray.reverse() // 数据准备好后,在主线程刷新tableView(UI操作必须在主线程) DispatchQueue.main.async { self.charitiesSupportedTable.reloadData() } }) // 加载总筹款数据 let totalMoneyRef = Database.database().reference().child("UsersViews").child(email1).child("totalMoneyRaised") totalMoneyRef.observe(.value, with: { [weak self] snapshot in guard let self = self, let totalMoney = snapshot.value as? Double else { return } DispatchQueue.main.async { self.totalMoneyLabel.text = String(format: "$%.02f", totalMoney) } }) // 加载支持的慈善机构数量(如果行数依赖这个值) let charitiesSupportedRef = Database.database().reference().child("UsersViews").child(email1).child("charitiesSupported") charitiesSupportedRef.observe(.value, with: { [weak self] snapshot in guard let self = self, let myDictionary = snapshot.value as? [String: Any] else { return } self.myCharities.removeAll() for value in myDictionary.values { if let charity = value as? String { self.myCharities.append(charity) } } self.charitiesCount = self.myCharities.count // 如果行数依赖这个count,这里也要触发刷新 DispatchQueue.main.async { self.charitiesSupportedTable.reloadData() } }) }
3. 修正行数计算逻辑
确保numberOfRowsInSection返回的是你实际要显示的数组长度,而不是模糊的charitiesCount + 2:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { // 如果你要显示过往帖子的数据,直接返回数组长度 return charOfPrevPostsArray.count // 如果确实需要返回charitiesCount + 2,确保上面的慈善机构数量更新后会触发刷新 // return charitiesCount + 2 }
额外优化建议
- 提前注册cell:在
viewDidLoad里添加charitiesSupportedTable.register(UITableViewCell.self, forCellReuseIdentifier: "SupportedCell"),这样就不用判断cell是否为nil了,复用更高效。 - 避免实时监听(如果不需要):如果你的数据不需要实时更新,把
observe(.value)换成observeSingleEvent(of: .value),减少不必要的回调和能耗。 - 防止循环引用:用
[weak self]捕获self,避免Firebase回调导致内存泄漏。
内容的提问来源于stack exchange,提问作者Nate
相关产品推荐
相关产品推荐

