[英]Converting to Swift 3 broke custom class encoding/decoding
So I've just converted a small app from Swift 2.2 to Swift 3. I've gotten rid of the usual errors and bits of mop up required after the auto converter but I've got a run time issue that I can't work out. 所以我刚刚将一个小应用程序从Swift 2.2转换为Swift 3.我已经摆脱了自动转换器后所需的通常错误和拖把,但我有一个运行时问题我无法工作出。
I've got a custom class that I am saving to NSUserDefaults with the NSCoding protocol. 我有一个自定义类,我使用NSCoding协议保存到NSUserDefaults。 When I try to decode the encoded object from NSUserDefaults it fails on the guard let duration = decoder.decodeObject(forKey: "duration") as? Int
当我尝试从NSUserDefaults解码编码对象时,它在guard let duration = decoder.decodeObject(forKey: "duration") as? Int
上失败, guard let duration = decoder.decodeObject(forKey: "duration") as? Int
guard let duration = decoder.decodeObject(forKey: "duration") as? Int
line as duration is printing as nil. guard let duration = decoder.decodeObject(forKey: "duration") as? Int
行作为持续时间打印为零。 Decoding the title string works fine however so at least that line of the encode function is working correctly. 解码标题字符串工作正常,但至少编码函数的行正常工作。
This worked fine in 2.2 and I can't find anything indicating that Swift 3 made changes to the NSCoding. 这在2.2中运行良好,我找不到任何迹象表明Swift 3对NSCoding进行了更改。 Any help would be much appreciated. 任何帮助将非常感激。
class TimerModel: NSObject, NSCoding, AVAudioPlayerDelegate {
var name: String
var active: Bool
var paused: Bool
var duration: Int
var remainingWhenPaused: Int?
var timerEndTime: Date?
var timerStartTime: Date?
var audioAlert: AlertNoise
var UUID: String
var colorScheme: BaseColor
var alarmRepetitions: Int
var timerRepetitions: Int
var currentTimerRepetition: Int
var audioPlaying: Bool
var player: AVAudioPlayer = AVAudioPlayer()
var countDownTimer: Timer = Timer()
var delegate: timerProtocol? = nil
init(withName name: String, duration: Int, UUID: String, color: BaseColor, alertNoise: AlertNoise, timerRepetitions: Int, alarmRepetitions: Int) {
self.name = name
self.active = false
self.paused = false
self.duration = duration
self.UUID = UUID
self.audioAlert = alertNoise
self.colorScheme = color
self.alarmRepetitions = alarmRepetitions
self.audioPlaying = false
self.timerRepetitions = timerRepetitions
self.currentTimerRepetition = 0
super.init()
}
convenience override init() {
self.init(withName: "Tap Timer 1", duration: 10, UUID: Foundation.UUID().uuidString, color: .Red, alertNoise: .ChurchBell, timerRepetitions: 1, alarmRepetitions: 0)
}
// MARK: NSCoding
required convenience init? (coder decoder: NSCoder) {
print("in init coder:")
print("Name: \(decoder.decodeObject(forKey: "name"))")
print("Duration: \(decoder.decodeObject(forKey: "duration"))")
guard let name = decoder.decodeObject(forKey: "name") as? String
else {
print("init coder name guard failed")
return nil
}
guard let duration = decoder.decodeObject(forKey: "duration") as? Int
else {
print("init coder duration guard failed")
print("duration: \(decoder.decodeObject(forKey: "duration"))")
return nil
}
guard let audioAlertRawValue = decoder.decodeObject(forKey: "audioAlert") as? String
else {
print("init coder audioAlert guard failed")
return nil
}
guard let UUID = decoder.decodeObject(forKey: "UUID") as? String
else {
print("init coder UUID guard failed")
return nil
}
guard let colorSchemeRawValue = decoder.decodeObject(forKey: "colorScheme") as? String
else {
print("init coder colorScheme guard failed")
return nil
}
guard let alarmRepetitions = decoder.decodeObject(forKey: "alarmRepetitions") as? Int
else {
print("init coder alarmRepetitions guard failed")
return nil
}
guard let timerRepetitions = decoder.decodeObject(forKey: "timerRepetitions") as? Int
else {
print("init coder timerRepetitions guard failed")
return nil
}
guard let audioAlert = AlertNoise(rawValue: audioAlertRawValue)
else{
print("No AlertNoise rawValue case found")
return nil
}
guard let colorScheme = BaseColor(rawValue: colorSchemeRawValue)
else{
print("No BaseColor rawValue case found")
return nil
}
print("initCoder guards passed, initing timer")
print("\(name), \(duration), \(UUID), \(colorScheme), \(audioAlert), \(timerRepetitions), \(alarmRepetitions)")
self.init(withName: name, duration: duration, UUID: UUID, color: colorScheme, alertNoise: audioAlert, timerRepetitions: timerRepetitions, alarmRepetitions: alarmRepetitions)
}
func encode(with aCoder: NSCoder) {
aCoder.encode(self.name, forKey: "name")
aCoder.encode(self.duration, forKey: "duration")
aCoder.encode(self.audioAlert.rawValue, forKey: "audioAlert")
aCoder.encode(self.UUID, forKey: "UUID")
aCoder.encode(self.colorScheme.rawValue, forKey: "colorScheme")
aCoder.encode(self.alarmRepetitions, forKey: "alarmRepetitions")
aCoder.encode(self.timerRepetitions, forKey: "timerRepetitions")
}
So it seems the solution is simple if a little unintuitive. 因此,如果有点不直观,似乎解决方案很简单。
So I encoded the class ivars with the general method encode(self.ivar, forKey: "keyName")
however if that ivar is an int it needs to be decoded with decodeInteger(forKey: "keyName")
- this entails getting rid of the guard statements as well since this method return an non-optional. 所以我用常规方法encode(self.ivar, forKey: "keyName")
类ivars encode(self.ivar, forKey: "keyName")
但是如果那个ivar是一个int,它需要用decodeInteger(forKey: "keyName")
解码decodeInteger(forKey: "keyName")
- 这需要摆脱它保护语句也是因为这个方法返回一个非可选的。 Seems odd to have to decode with the integer specific method if it was decoded with the generalist method - this wasn't the case in Swift 2.2. 如果使用通才方法解码,则看起来很奇怪必须使用整数特定方法进行解码 - 在Swift 2.2中并非如此。
Excellent answer by SimonBarker and it solved the same issue I had. SimonBarker给出了很好的答案,它解决了我遇到的同样问题。 I eventually applied his solution to my own code and revised it too so that encoding is done with the generalist method. 我最终将他的解决方案应用于我自己的代码并对其进行了修改,以便使用通用方法完成编码。 You can "force" encoding with the generalist method by using: 您可以使用通用方法“强制”编码:
func encode(_ objv: Any?, forKey key: String)
So in your code you could use: 所以在你的代码中你可以使用:
aCoder.encode(self.name as Any?, forKey: "name")
That way self.name is encoded as an object and does not break your existing code, namely: decoder.decodeObject(forKey: "name") as? 这样self.name被编码为一个对象,并不会破坏你现有的代码,即:decoder.decodeObject(forKey:“name”)为? String 串
This might not be the most elegant solution, but at least it worked for me without the need to change code that worked beautifully in Swift 2.3, but which was broken in Swift 3... 这可能不是最优雅的解决方案,但至少它对我有用而无需更改在Swift 2.3中运行良好的代码,但在Swift 3中被破坏了......
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.