Swift Firebase单函数获取多组数据计数异常问题求助
解决Firebase获取数据时重复累加计数的问题
你遇到的问题核心非常明确:你把获取user-posts的请求放在了user-plan的子节点循环里。也就是说,每遍历一个user-plan的条目,就会发起一次user-posts的数据请求,每次请求到数据后又把所有post的key追加到totalPosts数组中——最终totalPosts的长度就是14×18=252,完全符合你看到的结果。
下面给你两种解决方案,根据你是否需要保存节点key来选择:
方案一:仅需要计数(不需要保存节点key)
这种情况最简洁,直接用Firebase的childrenCount属性获取条目数量,不需要遍历子节点:
func firebaseRetrieve() { guard let uid = Auth.auth().currentUser?.uid else {return} // 第一步:获取user-plan的条目数量 let planRef = DB_BASE.child("user-plan").child(uid) planRef.observeSingleEvent(of: .value, with: { [weak self] planSnapshot in guard let self = self else { return } let planCount = Double(planSnapshot.childrenCount) // 处理plan数量为0的情况,避免除以0崩溃 guard planCount > 0 else { self.fraction = 0 // 这里调用更新进度条的函数 return } // 第二步:获取user-posts的条目数量(仅请求一次) let postRef = DB_BASE.child("user-posts").child(uid) postRef.observeSingleEvent(of: .value, with: { [weak self] postSnapshot in guard let self = self else { return } let postCount = Double(postSnapshot.childrenCount) self.fraction = postCount / planCount // 此处传入另一个函数,用fraction值更新进度条 }) }) }
改进点说明:
- 去掉了冗余的子节点遍历,用
childrenCount直接获取数量,代码更高效 - 将
user-posts的请求移到user-plan数据获取完成的回调外层,只请求一次,彻底避免重复累加 - 增加
[weak self]避免循环引用,符合Swift内存管理规范 - 增加了plan数量为0的边界处理,防止运行时崩溃
方案二:需要保存节点key(比如后续要用到这些key)
如果你必须保存user-plan和user-posts的节点key,那可以先完整收集完plan的key,再去收集posts的key,确保posts只被收集一次:
func firebaseRetrieve() { guard let uid = Auth.auth().currentUser?.uid else {return} var tempPlans: [String] = [] var tempPosts: [String] = [] // 第一步:收集所有user-plan的key let planRef = DB_BASE.child("user-plan").child(uid) planRef.observeSingleEvent(of: .value, with: { [weak self] planSnapshot in guard let self = self else { return } for child in planSnapshot.children { if let snap = child as? DataSnapshot { tempPlans.append(snap.key) } } let planCount = Double(tempPlans.count) guard planCount > 0 else { self.fraction = 0 // 更新进度条 return } // 第二步:收集所有user-posts的key(仅收集一次) let postRef = DB_BASE.child("user-posts").child(uid) postRef.observeSingleEvent(of: .value, with: { [weak self] postSnapshot in guard let self = self else { return } for child in postSnapshot.children { if let snaps = child as? DataSnapshot { tempPosts.append(snaps.key) } } let postCount = Double(tempPosts.count) self.fraction = postCount / planCount // 更新进度条 // 把临时数组赋值给实例变量(如果需要的话) self.totalPlans = tempPlans self.totalPosts = tempPosts }) }) }
改进点说明:
- 用临时数组先收集完所有plan的key,再去请求posts数据
- posts的key仅被收集一次,不会因为plan的数量重复累加
- 同样处理了边界情况和内存引用问题
内容的提问来源于stack exchange,提问作者PlanB
相关产品推荐
相关产品推荐

