Firestore中QuerySnapshot运行报错:GRPC_LINUX_EPOLL未定义
问题分析与解决方案
首先要明确:你看到的 "Skipping epollsig becuase GRPC_LINUX_EPOLL is not defined" 并不是代码错误,它只是GRPC库(Firebase依赖的底层网络库)的调试日志输出,和你打印字段的次数只是巧合关联,完全可以忽略,或者在Xcode日志过滤器中屏蔽这条日志。
真正需要修正的是你的数据解析、打印逻辑,以及随机文档查询的实现,下面逐一说明:
1. 模型类的问题:强制解包风险 + 打印无意义内容
你当前的randomModel类存在两个问题:
- 用
!强制解包Firestore字段值,如果字段缺失、类型不匹配,会直接导致崩溃 - 没有实现
CustomStringConvertible,直接打印模型数组只会输出对象内存地址,看不到实际数据
修改后的模型类
// 类名首字母大写符合Swift命名规范 class RandomModel { private(set) var quote: String private(set) var randomInt: Int private(set) var approved: Bool private(set) var source: String init(approved: Bool, quote: String, randomInt: Int, source: String) { self.approved = approved self.quote = quote self.randomInt = randomInt self.source = source } class func parseData(snapshot: QuerySnapshot?) -> [RandomModel] { var quoteStructure = [RandomModel]() guard let snap = snapshot else { return quoteStructure } for document in snap.documents { let data = document.data() // 用可选绑定安全获取字段值,无效文档直接跳过 guard let quote = data[DATABASE_QUOTE_VALUE_FIELD] as? String, let randomInt = data[DATABASE_RANDOM_INTEGER_VALUE_FIELD] as? Int, let approved = data[DATABASE_APPROVED_VALUE_FIELD] as? Bool, let source = data[DATABASE_SOURCE_VALUE_FIELD] as? String else { debugPrint("跳过无效文档:\(document.documentID),字段缺失或类型不匹配") continue } let parsedData = RandomModel(approved: approved, quote: quote, randomInt: randomInt, source: source) quoteStructure.append(parsedData) } return quoteStructure } } // 实现CustomStringConvertible,让打印时显示实际字段内容 extension RandomModel: CustomStringConvertible { var description: String { return "RandomModel(quote: \"\(quote)\", randomInt: \(randomInt), approved: \(approved), source: \"\(source)\")" } }
2. 随机文档查询逻辑错误
你当前的查询whereField(DATABASE_RANDOM_INTEGER_VALUE_FIELD, isGreaterThan: 1).order(by: "randomInt").limit(to: 1)并不是真正的随机,它每次都会取randomInt大于1的第一个文档,结果是固定的。
修正后的随机查询实现
这个方案通过先获取集合中randomInt的最大值,生成一个随机数,再查询对应区间的文档,确保每次返回随机结果:
@IBAction func generatePressed(_ sender: Any) { let quoteRef = Firestore.firestore().collection("Quotes") // 第一步:获取集合中最大的randomInt值 quoteRef.order(by: DATABASE_RANDOM_INTEGER_VALUE_FIELD, descending: true).limit(to: 1).getDocument { (docSnapshot, error) in if let err = error { debugPrint("获取max randomInt失败:\(err)") return } guard let maxRandomInt = docSnapshot?.data()?[DATABASE_RANDOM_INTEGER_VALUE_FIELD] as? Int else { debugPrint("无法获取max randomInt") return } // 生成1到maxRandomInt之间的随机数 let randomValue = Int.random(in: 1...maxRandomInt) // 第二步:查询大于等于随机数的第一个文档 let query = quoteRef.whereField(DATABASE_RANDOM_INTEGER_VALUE_FIELD, isGreaterThanOrEqualTo: randomValue) .order(by: DATABASE_RANDOM_INTEGER_VALUE_FIELD) .limit(to: 1) query.getDocument { (snapshot, error) in if let err = error { debugPrint("获取随机文档失败:\(err)") return } // 如果没找到, fallback到查询小于随机数的最后一个文档(处理随机数是最大值的情况) if snapshot == nil || snapshot?.exists == false { let fallbackQuery = quoteRef.whereField(DATABASE_RANDOM_INTEGER_VALUE_FIELD, isLessThan: randomValue) .order(by: DATABASE_RANDOM_INTEGER_VALUE_FIELD, descending: true) .limit(to: 1) fallbackQuery.getDocument { (fallbackSnapshot, fallbackError) in if let err = fallbackError { debugPrint("获取备选随机文档失败:\(err)") return } // 解析并打印数据 if let fallbackSnap = fallbackSnapshot { let querySnapshot = QuerySnapshot(documents: [fallbackSnap], metadata: SnapshotMetadata()) let models = RandomModel.parseData(snapshot: querySnapshot) print(models) } } } else { // 解析并打印数据 if let snap = snapshot { let querySnapshot = QuerySnapshot(documents: [snap], metadata: SnapshotMetadata()) let models = RandomModel.parseData(snapshot: querySnapshot) print(models) } } } } }
3. 额外说明
- 如果你的
randomInt字段是在文档创建时生成的随机数,确保它的取值范围覆盖所有文档,这样随机查询的效果才会均匀 - 如果你不需要实时监听文档变化,用
getDocument替代addSnapshotListener更合适,避免不必要的实时更新监听
内容的提问来源于stack exchange,提问作者TVProducer
相关产品推荐
相关产品推荐

