引用类型/子类,和变更夫特4可编码与编码器/解码器(Reference Types/Subclass

2019-09-27 18:15发布

我努力理解类/引用类型行为,以及如何这涉及到改变,因为我尝试升级和斯威夫特4使用可编码减少代码。

我有两个类 - 超类的所有数据,这将是持久的,而我保存到UserDefaults(一个地名和坐标字符串),以及包含额外的一个子类,临时的信息,我不需要(气象数据对于超坐标)。

在斯威夫特3我用来保存这样的数据:

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它也包括所需要的初始化解码器编码的功能。 这一切都工作得很好。

在尝试切换到斯威夫特4可编码我修改父类,以符合可编码。 超类,现在只有一个基本的初始化,没有从斯威夫特3.编码器/解码器的东西没有KeyedArchiving用这种新方法(下)发生。 子类保持不变。 不幸的是我崩溃就行,我试试吗? encoder.encode [给予线程1:EXC_BAD_ACCESS(码= 1,地址= 0×10)]。 我的假设是,所述编码器被越来越有相同的附图类型混淆,其中一个是超类,并且一个子(subClassArray [0] === superClassArray [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,然后使用:superClassArray = subClassArray,如上所示,我与单线替换这样的:

let superClassArray: [SuperClass] = subClassArray.map{SuperClass(name:  $0.name, coordinates: $0.coordinates)}

这工作。 再次,假设是因为我传递中的值的类引用类型的内部与还未对superClassArray = subClassArray。 此外,如预期的,subClassArray [0] === superClassArray [0]是假

那么,为什么即使我使用的线superClassArray = subClassArray的让superClassData = NSKeyedArchiver.archivedData(withRootObject:superClassArray)之前的“老东西”,在斯威夫特3工作? 我在本质上实现通过创建斯威夫特4数组,将其与老雨燕3编码器/解码器发生同样的结果? 是循环/娱乐

谢谢!

Answer 1:

多态持久性似乎是由设计打破

错误报告SR-5331报价,他们对他们得到的回应雷达。

不同于现有NSCoding API(的NSKeyedArchiver),新的SWIFT 4可编码实现不写出有关编码类型转换为生成档案,两者的灵活性和安全类型的信息。 这样,在解码时,该API只能用混凝土键入您提供以解码值(在你的情况下,超型)。

这是设计的-如果你需要做到这一点需要活力,我们建议您采用NSSecureCoding和使用的NSKeyedArchiver / NSKeyedUnarchiver

我不以为然,所有发光的文章,可编码是回答一些我的祈祷具有思想。 一组平行作为对象工厂可编码的结构是一个解决办法,我在考虑,为了保持类型信息。

更新我已经写了使用管理再造态类的单一结构的样本。 可在GitHub上 。

我是不是能够得到它与子类轻松地工作。 然而,符合一个基础协议类可以应用Codable为默认编码。 回购既包含密钥和无密钥的方法。 比较简单的是无锁,下面复制

// 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()


文章来源: Reference Types/Subclassing, and Changes to Swift 4 Codable & encoder/decoders