Swift如何从Firebase获取所有用户数据并解决属性随机报错问题
问题成因
- User实例复用导致数据错乱:你将
User()实例的创建逻辑写在了.observe(.childAdded)的异步回调外部,Firebase的childAdded事件会为每一个用户节点单独触发一次回调,所有回调都会修改同一个全局User实例的属性,且数组中追加的都是指向该实例的引用。异步回调的执行时序不可控,会出现前一次属性赋值未完成、后一次赋值就启动的情况,导致属性值错乱,随机触发KVC匹配错误。 - Swift属性未对Objective-C runtime可见:你使用的
setValuesForKeys(_:)是Objective-C的KVC接口,Swift 4及以上版本默认不会自动将Swift属性暴露给ObjC runtime,KVC无法识别你定义的email、first_name等属性,会触发未定义键的崩溃。
解决方法
需要同时修改User类定义和数据获取逻辑,具体修改如下:
- 修正User类的属性修饰,让KVC可以识别属性,两种方案二选一即可:
// 方案1:逐个给属性添加@objc修饰 public class User : NSObject{ @objc var email: String? @objc var first_name: String? @objc var last_name: String? @objc var profile_picture: String? } // 方案2:给整个类添加@objcMembers修饰,批量暴露所有属性给ObjC runtime @objcMembers public class User : NSObject{ var email: String? var first_name: String? var last_name: String? var profile_picture: String? }
- 将User实例的创建逻辑挪到回调闭包内部,每次回调都生成全新的实例,避免复用:
var users = [User]() func fetchUserData () { Database.database().reference().child("users").observe(.childAdded, with: { snapshot in if let dictionary = snapshot.value as? [String: AnyObject] { // 每次触发回调都生成新的User实例 let user = User() user.setValuesForKeys(dictionary) self.users.append(user) } }, withCancel: nil) }
优化建议(可选)
如果不想依赖KVC的自动赋值,可以改用手动赋值的方式,容错性更高,就算数据库字段和类属性不匹配也不会触发崩溃:
if let dictionary = snapshot.value as? [String: AnyObject] { let user = User() user.email = dictionary["email"] as? String user.first_name = dictionary["first_name"] as? String user.last_name = dictionary["last_name"] as? String user.profile_picture = dictionary["profile_picture"] as? String self.users.append(user) }
内容的提问来源于stack exchange,提问作者Omar
相关产品推荐
相关产品推荐

