You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:51:41