如何通过Decodable协议初始化Realm对象?附代码示例
搞定Realm对象继承+Decodable实现的问题
先给你捋捋原代码里的几个明显坑:
required init() { self.init() }这是纯纯的递归调用啊,一运行直接崩溃,得改成调用父类的空初始化super.init()- 那些
init(realm:schema:)和init(value:schema:)是Realm内部用的方法,咱们自己不用实现,删掉就行,多写反而容易出问题 - Realm的
List类型默认不支持Decodable解码,得自己写逻辑把JSON数组转成List - 子类B得处理自己的CodingKeys,还要在解码时先搞定父类的属性,再处理自己的
下面是修正后的完整代码,带注释说明:
父类A的正确实现
import RealmSwift class A: Object, Decodable { // Realm的List属性,默认是空的 let listA = List<Int>() // 只保留Realm要求的空初始化,调用父类的init required override init() { super.init() } // 对应JSON里的字段名,这里假设JSON里的键是"list_a",你可以根据实际情况改 private enum CodingKeys: String, CodingKey { case listA = "list_a" } // Decodable要求的初始化方法 required convenience init(from decoder: Decoder) throws { self.init() let container = try decoder.container(keyedBy: CodingKeys.self) // 把JSON数组解码成Swift数组,再转成Realm的List if let intArray = try container.decodeIfPresent([Int].self, forKey: .listA) { listA.append(objectsIn: intArray) } } }
子类B的正确实现
class B: A { let listB = List<Int>() // 子类自己的CodingKeys,对应JSON里的"list_b" private enum CodingKeys: String, CodingKey { case listB = "list_b" } // 子类的空初始化,调用父类init required init() { super.init() } // 子类的解码初始化,要先处理父类的属性 required convenience init(from decoder: Decoder) throws { self.init() let container = try decoder.container(keyedBy: CodingKeys.self) // 先调用父类的解码逻辑,确保listA被正确初始化 try super.init(from: decoder) // 处理自己的listB属性,和父类逻辑一样 if let intArray = try container.decodeIfPresent([Int].self, forKey: .listB) { listB.append(objectsIn: intArray) } } }
几个关键要点说明
- 递归初始化问题:原代码里的
self.init()会无限循环调用自己,必须换成super.init()来调用Realm Object的内置空初始化 - List解码逻辑:因为Realm的List没有默认实现Decodable,所以我们先把JSON里的数组转成Swift标准数组,再用
append(objectsIn:)把元素批量添加到List中 - 继承解码顺序:子类解码时一定要先调用
super.init(from:)处理父类的属性,不然父类的List会是空的 - 字段可选性:如果JSON里的数组字段是必传的,把
decodeIfPresent改成decode就行,这样字段缺失时会直接抛出解码错误,方便排查问题
内容的提问来源于stack exchange,提问作者J. Doe
相关产品推荐
相关产品推荐

