参考类型/子类化,以及对Swift 4编码表和;编码器/解码器
在我尝试使用Swift 4中的Codable升级和减少代码时,我正在努力理解类/引用类型行为以及这与更改的关系 我有两个类——一个超类,包含所有将被持久化并保存为UserDefaults的数据(一个带有坐标的地名和字符串),另一个子类包含我不需要的额外临时信息(超类坐标的天气数据) 在Swift 3中,我曾经这样保存数据:参考类型/子类化,以及对Swift 4编码表和;编码器/解码器,swift,reference-type,codable,userdefaults,Swift,Reference Type,Codable,Userdefaults,在我尝试使用Swift 4中的Codable升级和减少代码时,我正在努力理解类/引用类型行为以及这与更改的关系 我有两个类——一个超类,包含所有将被持久化并保存为UserDefaults的数据(一个带有坐标的地名和字符串),另一个子类包含我不需要的额外临时信息(超类坐标的天气数据) 在Swift 3中,我曾经这样保存数据: func saveUserDefaults() { var superClassArray = [SuperClass]() // subClassArray
func saveUserDefaults() {
var superClassArray = [SuperClass]()
// subClassArray is of type [SubClass] and contains more data per element.
superClassArray = subClassArray
let superClassData = NSKeyedArchiver.archivedData(withRootObject: superClassArray)
UserDefaults.standard.set(superClassData, forKey: " superClassData")
}
符合NSObject和NSCoding的超类
它还包括所需的init解码器和编码功能。
一切都很顺利
在尝试切换到swift4&codable时,我修改了超类以符合codable。
超类现在只有一个基本的初始值设定项,而Swift 3中的编码器/解码器都没有。这种新方法(见下文)不会产生关键的归档。子类保持不变。不幸的是,我在我尝试的地方撞车了?encoder.encode[给出线程1:EXC\u BAD\u访问(代码=1,地址=0x10)]。我的假设是编码器与相同的引用类型混淆,其中一个是超类,另一个是子类(subassarray[0]===超类数组[0]为真)。
我想这可能有用:
func saveUserDefaults() {
var superClassArray = [SuperClass]()
superClassArray = subClassArray
// assumption was that the subclass would only contain parts of the superclass & wouldn't produce an error when being encoded
let encoder = JSONEncoder()
if let encoded = try? encoder.encode(superClassArray){
UserDefaults.standard.set(encoded, forKey: " superClassArray ")
} else {
print("Save didn't work!")
}
}
然后,不是创建空的超类数组,而是使用:
superClassArray=subassarray,如上所示,我将其替换为一行:
let superClassArray: [SuperClass] = subClassArray.map{SuperClass(name: $0.name, coordinates: $0.coordinates)}
这很有效。同样,假设是因为我在类引用类型中传递值&还没有生成superClassArray=subassarray。此外,正如预期的那样,子类数组[0]==超类数组[0]为false
那么,为什么Swift 3中的“老东西”起作用了,尽管我在let superClassData=NSKeyedArchiver.archivedData(withRootObject:superClassArray)之前使用了行superClassArray=subassarray
? 我是否通过在Swift 4中创建与旧Swift 3编码器/解码器相同的阵列来实现相同的结果?是环路/娱乐吗
谢谢 多态持久性似乎被设计破坏了 错误报告引用了他们在报告中得到的响应 与现有的nscodingapi(NSKeyedArchiver)不同,出于灵活性和安全性考虑,新的swift4可编码实现不会将编码类型的类型信息写入生成的归档中。因此,在解码时,API只能使用您提供的具体类型来解码值(在您的情况下,是超类类型) 这是经过设计的-如果您需要这样做所需的动态性,我们建议您采用NSSecureCodeding并使用NSKeyedArchiver/NSKeyedUnachiver 我没有被打动,从所有发光的文章中,我认为Codable是我一些祈祷的答案。作为对象工厂的一组并行可编码结构是我正在考虑的一个解决方案,以保留类型信息 更新我使用一个结构编写了一个示例,该结构管理重新创建多态类。可于下载 我没有能够通过子类化轻松地让它工作。但是,符合基本协议的类可以对默认编码应用
Codable
。回购协议包含关键和非关键方法。更简单的是
更新2020-04年经验
这种方法仍然比使用Codable更灵活、更优越,但要花费更多的程序员时间。它在Touchgram应用程序中被大量使用,该应用程序在iMessage中提供丰富的交互式文档
在那里,我需要编码多个多态层次结构,包括不同的传感器和动作。通过存储解码器的签名,它不仅提供了子类化,还允许我将旧解码器保留在代码库中,以便旧消息仍然兼容。多态持久性似乎被设计破坏了 错误报告引用了他们在报告中得到的响应 与现有的nscodingapi(NSKeyedArchiver)不同,出于灵活性和安全性考虑,新的swift4可编码实现不会将编码类型的类型信息写入生成的归档中。因此,在解码时,API只能使用您提供的具体类型来解码值(在您的情况下,是超类类型) 这是经过设计的-如果您需要这样做所需的动态性,我们建议您采用NSSecureCodeding并使用NSKeyedArchiver/NSKeyedUnachiver 我没有被打动,从所有发光的文章中,我认为Codable是我一些祈祷的答案。作为对象工厂的一组并行可编码结构是我正在考虑的一个解决方案,以保留类型信息 更新我使用一个结构编写了一个示例,该结构管理重新创建多态类。可于下载 我没有能够通过子类化轻松地让它工作。但是,符合基本协议的类可以对默认编码应用
Codable
。回购协议包含关键和非关键方法。更简单的是
更新2020-04年经验
这种方法仍然比使用Codable更灵活、更优越,但要花费更多的程序员时间。它在Touchgram应用程序中被大量使用,该应用程序在iMessage中提供丰富的交互式文档
在那里,我需要编码多个多态层次结构,包括不同的传感器和动作。通过存储解码器的签名,它不仅提供了子类化,还允许我在代码库中保留较旧的解码器,以便旧消息仍然兼容。捕获
jsonecoder
引发的错误并将其打印出来。这将更好地了解编码失败的原因。此外,如果您可以包含类的定义(或演示该问题的简化版本),则更有可能有人能够
// Demo of a polymorphic hierarchy of different classes implementing a protocol
// and still being Codable
// This variant uses unkeyed containers so less data is pushed into the encoded form.
import Foundation
protocol BaseBeast {
func move() -> String
func type() -> Int
var name: String { get }
}
class DumbBeast : BaseBeast, Codable {
static let polyType = 0
func type() -> Int { return DumbBeast.polyType }
var name:String
init(name:String) { self.name = name }
func move() -> String { return "\(name) Sits there looking stupid" }
}
class Flyer : BaseBeast, Codable {
static let polyType = 1
func type() -> Int { return Flyer.polyType }
var name:String
let maxAltitude:Int
init(name:String, maxAltitude:Int) {
self.maxAltitude = maxAltitude
self.name = name
}
func move() -> String { return "\(name) Flies up to \(maxAltitude)"}
}
class Walker : BaseBeast, Codable {
static let polyType = 2
func type() -> Int { return Walker.polyType }
var name:String
let numLegs: Int
let hasTail: Bool
init(name:String, legs:Int=4, hasTail:Bool=true) {
self.numLegs = legs
self.hasTail = hasTail
self.name = name
}
func move() -> String {
if numLegs == 0 {
return "\(name) Wriggles on its belly"
}
let maybeWaggle = hasTail ? "wagging its tail" : ""
return "\(name) Runs on \(numLegs) legs \(maybeWaggle)"
}
}
// Uses an explicit index we decode first, to select factory function used to decode polymorphic type
// This is in contrast to the current "traditional" method where decoding is attempted and fails for each type
// This pattern of "leading type code" can be used in more general encoding situations, not just with Codable
//: **WARNING** there is one vulnerable practice here - we rely on the BaseBeast types having a typeCode which
//: is a valid index into the arrays `encoders` and `factories`
struct CodableRef : Codable {
let refTo:BaseBeast //In C++ would use an operator to transparently cast CodableRef to BaseBeast
typealias EncContainer = UnkeyedEncodingContainer
typealias DecContainer = UnkeyedDecodingContainer
typealias BeastEnc = (inout EncContainer, BaseBeast) throws -> ()
typealias BeastDec = (inout DecContainer) throws -> BaseBeast
static var encoders:[BeastEnc] = [
{(e, b) in try e.encode(b as! DumbBeast)},
{(e, b) in try e.encode(b as! Flyer)},
{(e, b) in try e.encode(b as! Walker)}
]
static var factories:[BeastDec] = [
{(d) in try d.decode(DumbBeast.self)},
{(d) in try d.decode(Flyer.self)},
{(d) in try d.decode(Walker.self)}
]
init(refTo:BaseBeast) {
self.refTo = refTo
}
init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
let typeCode = try container.decode(Int.self)
self.refTo = try CodableRef.factories[typeCode](&container)
}
func encode(to encoder: Encoder) throws {
var container = encoder.unkeyedContainer()
let typeCode = self.refTo.type()
try container.encode(typeCode)
try CodableRef.encoders[typeCode](&container, refTo)
}
}
struct Zoo : Codable {
var creatures = [CodableRef]()
init(creatures:[BaseBeast]) {
self.creatures = creatures.map {CodableRef(refTo:$0)}
}
func dump() {
creatures.forEach { print($0.refTo.move()) }
}
}
//: ---- Demo of encoding and decoding working ----
let startZoo = Zoo(creatures: [
DumbBeast(name:"Rock"),
Flyer(name:"Kookaburra", maxAltitude:5000),
Walker(name:"Snake", legs:0),
Walker(name:"Doggie", legs:4),
Walker(name:"Geek", legs:2, hasTail:false)
])
startZoo.dump()
print("---------\ntesting JSON\n")
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let encData = try encoder.encode(startZoo)
print(String(data:encData, encoding:.utf8)!)
let decodedZoo = try JSONDecoder().decode(Zoo.self, from: encData)
print ("\n------------\nAfter decoding")
decodedZoo.dump()