- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
当我尝试在 Swift 4 中使用 Codable 升级和减少代码时,我正在努力理解类/引用类型的行为以及这与更改的关系。
我有两个类——一个包含所有持久数据并保存到 UserDefaults(地名和带坐标的字符串)的父类(super class),以及一个包含我不需要的附加临时信息的子类(父类(super class)坐标的天气数据)。
在 Swift 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")
}
父类(super class)符合 NSObject 和 NSCoding它还包括所需的初始化解码器和编码功能。一切正常。
在尝试切换到 Swift 4 和 Codable 时,我修改了 SuperClass 以符合 Codable。SuperClass 现在只有一个基本的初始化器,没有来自 Swift 3 的编码器/解码器的东西。这种新方法没有 KeyedArchiving 发生(如下)。子类保持不变。不幸的是我在网上崩溃了 我试试? encoder.encode [给出线程 1:EXC_BAD_ACCESS(代码=1,地址=0x10)]。我的假设是编码器与相同的引用类型混淆,其中一个是父类(super class),一个是子类(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] 为 false
那么为什么 Swift 3 中的“旧东西”可以工作,即使我在 let superClassData = NSKeyedArchiver.archivedData(withRootObject: superClassArray) 之前使用了行 superClassArray = subClassArray?通过在旧的 Swift 3 编码器/解码器中发生的 Swift 4 中创建数组,我是否基本上实现了相同的结果?是循环/娱乐
谢谢!
最佳答案
多态持久性似乎被设计破坏。
错误报告 SR-5331引用他们在 Radar. 上得到的回应
与现有的 NSCoding API (NSKeyedArchiver) 不同,为了灵 active 和安全性,新的 Swift 4 Codable 实现不会将有关编码类型的类型信息写入生成的存档中。因此,在解码时,API 只能使用您提供的具体类型来解码值(在您的情况下,是父类(super class)类型)。
这是设计使然——如果您需要执行此操作所需的动态性,我们建议您采用 NSSecureCoding 并使用 NSKeyedArchiver/NSKeyedUnarchiver
我不为所动,从所有热烈的文章中想到 Codable 是我的一些祈祷的答案。一组并行的 Codable 结构充当对象工厂是我正在考虑的一种解决方法,以保留类型信息。
更新 我已经使用管理重新创建多态类的单个结构编写了一个示例。在 GitHub 上可用.
我无法通过子类让它轻松工作。但是,符合基本协议(protocol)的类可以应用 Codable
作为默认编码。 repo 协议(protocol)包含键控和非键控方法。更简单的是unkeyed, copied below
// 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()
这种方法比使用 Codable 更加灵活和优越,但代价是更多的程序员时间。它在 Touchgram 应用程序中被大量使用,该应用程序在 iMessage 中提供丰富的交互式文档。
在那里,我需要对多个多态层次结构进行编码,包括不同的传感器和 Action 。通过存储解码器的签名,它不仅提供了子类化功能,还允许我在代码库中保留较旧的解码器,以便旧消息仍然兼容。
关于swift - 引用类型/子类化,以及对 Swift 4 Codable 和编码器/解码器的更改,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47021808/
有没有办法在 .swift 文件(编译成 .swift 模块)中声明函数,如下所示: 你好.swift func hello_world() { println("hello world")
我正在尝试使用 xmpp_messenger_ios 和 XMPPFramework 在 iOS 上执行 MUC 这是加入房间的代码。 func createOrJoinRoomOnXMPP()
我想在我的应用程序上创建一个 3D Touch 快捷方式,我已经完成了有关快捷方式本身的所有操作,它显示正确,带有文本和图标。 当我运行这个快捷方式时,我的应用程序崩溃了,因为 AppDelegate
我的代码如下: let assetTag = Expression("asset_tag") let query2 = mdm.select(mdm[assetTag],os, mac, lastRe
我的 swift 代码如下所示 Family.arrayTuple:[(String,String)]? = [] Family.arrayTupleStorage:String? Family.ar
这是我的 JSON,当我读取 ord 和 uniq 数据时出现错误 let response2 : [String: Any] = ["Response":["status":"SUCCESS","
我想将 swift 扩展文件移动到 swift 包中。但是,将文件移动到 swift 包后,我遇到了这种错误: "Type 'NSAttributedString' has no member 'ma
使用CocoaPods,我们可以设置以下配置: pod 'SourceModel', :configurations => ['Debug'] 有什么方法可以用 Swift Package Manag
我正在 Xcode 中开发一个 swift 项目。我将其称为主要项目。我大部分都在工作。我在日期选择器、日期范围和日期数学方面遇到了麻烦,因此我开始了另一个名为 StarEndDate 的项目,其中只
这是 ObjectiveC 代码: CCSprite *progress = [CCSprite spriteWithImageNamed:@"progress.png"]; mProgressBar
我正在创建一个命令行工具,在 Xcode 中使用 Swift。我想使用一个类似于 grunt 的配置文件确实如此,但我希望它是像 Swift 包管理器的 package.swift 文件那样的快速代码
我假设这意味着使用系统上安装的任何 swift 运行脚本:#!/usr/bin/swift 如何指定脚本适用的解释器版本? 最佳答案 Cato可用于此: #!/usr/bin/env cato 1.2
代码说完全没问题,没有错误,但是当我去运行模拟器的时候,会出现这样的字样: (Swift.LazyMapCollection (_base:[ ] 我正在尝试创建一个显示报价的报价应用。 这是导入
是否可以在运行 Swift(例如 Perfect、Vapor、Kitura 等)的服务器上使用 RealmSwift 并使用它来存储数据? (我正在考虑尝试将其作为另一种解决方案的替代方案,例如 no
我刚开始学习编程,正在尝试完成 Swift 编程书中的实验。 它要求““编写一个函数,通过比较两个 Rank 值的原始值来比较它们。” enum Rank: Int { case Ace = 1 ca
在您将此问题标记为重复之前,我检查了 this question 它对我不起作用。 如何修复这个错误: error: SWIFT_VERSION '5.0' is unsupported, suppo
从 Xcode 9.3 开始,我在我的模型中使用“Swift.ImplicitlyUnwrappedOptional.some”包裹了我的字符串变量 我不知道这是怎么发生的,但它毁了我的应用程序! 我
这个问题在这里已经有了答案: How to include .swift file from other .swift file in an immediate mode? (2 个答案) 关闭 6
我正在使用 Swift Package Manager 创建一个应用程序,我需要知道构建项目的配置,即 Debug 或 Release。我试图避免使用 .xcodeproj 文件。请有人让我知道这是否
有一个带有函数定义的文件bar.swift: func bar() { println("bar") } 以及一个以立即模式运行的脚本foo.swift: #!/usr/bin/xcrun s
我是一名优秀的程序员,十分优秀!