Firebase Transaction中MutableData误判定节点值为nil的异常问题
问题根因与解决方法
核心问题原因
- 这个问题和你把值转成Int、Double的类型转换操作完全无关,本质是Firebase实时数据库的事务执行机制导致的。
- 为了减少网络等待、提升事务响应速度,事务启动时不会阻塞等待拉取目标路径的服务端真实值,而是会先用一个本地初始空MutableData触发第一次事务块执行。此时你拿到的
mutableData.value本身就是nil,和数据库中实际存储的views:0没有任何关联。 - 你在第一次事务块执行时,检测到value为nil就直接返回了
TransactionResult.abort(),相当于事务从启动阶段就被强制终止,根本走不到后续和服务端同步、拉取真实值、第二次执行事务块的流程,自然永远拿不到数据库里存储的0值。
解决方法
核心调整逻辑:不要在第一次拿到空MutableData时就直接中止事务
原代码的错误,是把“事务初始状态的临时空值”和“数据库节点真实不存在的空值”做了等价判断,触发了过早的事务中止。
正确的事务代码参考:
let viewsPath = Database.database().reference() .child("posts") .child("postId_abc") .child("views") viewsPath.runTransactionBlock { mutableData in // 检测值为nil时,不要立刻abort,先让客户端完成服务端同步 guard let currentViews = mutableData.value as? Int else { // 返回成功,触发客户端向服务端拉取当前路径的真实值 return TransactionResult.success(withValue: mutableData) } // 等服务端同步完成后,这里就能正常拿到真实值(比如示例中的0) // 在这里执行后续的业务逻辑,比如阅读数累加 let updatedViews = currentViews + 1 mutableData.value = updatedViews return TransactionResult.success(withValue: mutableData) }
针对“节点不存在则中止”需求的优化
如果你确实需要实现“目标节点在服务端不存在就中止事务”的逻辑,可以按以下规则调整判断分支:
- 第一次拿到空MutableData时,不做中止判断,直接返回成功触发服务端同步
- 后续事务块执行时,如果依然检测到value为nil,才说明服务端的目标节点确实不存在,此时再返回
TransactionResult.abort()即可 - 只要任意一次事务块执行拿到了非空的真实值,就按照业务逻辑正常处理、返回成功
内容的提问来源于stack exchange,提问作者Lance Samaria
相关产品推荐
相关产品推荐

