Firebase runTransactionBlock致主线程延迟,如何优化UI无卡顿加载?
嗨,这个问题我之前做Firebase项目时也碰到过!核心问题在于你现在的操作顺序和事务逻辑都有可以优化的地方,咱们一步步来解决:
首先,先纠正事务逻辑的小问题
你当前的事务代码里直接用本地的_tapCount去设置服务器值,这其实违背了事务的设计初衷——多用户同时操作时,本地的_tapCount可能不是服务器的最新值,会导致计数不准确。正确的做法应该是基于服务器返回的当前值来原子递增。
优化方案:先跳转UI,再异步处理事务
用户点击按钮最在意的是页面能立刻跳转,而计数更新是后台的操作,完全不需要等它完成再跳转。我们可以调整操作顺序,同时优化事务的异步处理:
1. 修复并增强incrementTapCount方法
给方法加上completion回调,方便事务完成后更新本地UI:
func incrementTapCount(completion: ((Int?) -> Void)? = nil) { ref.runTransactionBlock { (currentData: MutableData) -> TransactionResult in // 从服务器获取当前的tapCount,默认值为0 var currentCount = currentData.childData(byAppendingPath: "tapCount").value as? Int ?? 0 currentCount += 1 currentData.childData(byAppendingPath: "tapCount").value = currentCount return TransactionResult.success(withValue: currentData) } andCompletionBlock: { (error, committed, snapshot) in if let error = error { print("事务更新失败:\(error.localizedDescription)") completion?(nil) return } // 事务提交成功后,获取最新计数并回调 if committed, let updatedCount = snapshot?.childSnapshot(forPath: "tapCount").value as? Int { self._tapCount = updatedCount completion?(updatedCount) } else { completion?(nil) } } }
2. 调整按钮点击事件的执行顺序
先跳转到目标ViewController,再异步执行事务更新,完全不阻塞UI:
func moreCommentAction(_ sender: AnyObject) { // 1. 优先执行页面跳转,这一步会立刻响应,用户不会感觉到卡顿 guard let moreCommentVC = sender.storyboard?.instantiateViewController(withIdentifier: "MoreCommentViewController") as? MoreCommentViewController else { return } moreCommentVC.globalPost = self.globalPost sender.present(moreCommentVC, animated: true, completion: nil) // 2. 异步处理Firebase计数更新,不影响UI流程 if Auth.auth().currentUser?.uid != nil { self.globalPost.incrementTapCount { [weak self] updatedCount in guard let self = self, let count = updatedCount else { return } // 在主线程更新UI(比如点赞按钮的标题),避免线程问题 DispatchQueue.main.async { let likeCount = self.globalPost.favoriteBoost + self.globalPost.favoriteDict.count + count self.likeButton.setTitle(" \(likeCount)", for: .normal) } } } }
为什么这样做能解决卡顿?
- 操作顺序优化:把用户最关心的页面跳转放在最前面,瞬间响应点击操作,消除卡顿感;
- 事务逻辑正确:基于服务器实时值递增,保证多用户场景下计数的准确性;
- 异步+主线程更新:Firebase的事务本身就是异步执行的,我们只需要在事务完成后,回到主线程更新UI,既不阻塞UI线程,也符合iOS的UI更新规则;
- 避免循环引用:用
[weak self]处理闭包引用,防止内存泄漏。
另外补充:不需要手动把事务放到后台线程,Firebase SDK已经在后台处理网络请求和事务计算了,我们只需要确保UI相关操作在主线程执行就好~
内容的提问来源于stack exchange,提问作者SBCoder
相关产品推荐
相关产品推荐

