简体   繁体   English

在 UserDefaults 上使用 NSKeyedArchiver 时重置属性

[英]Properties reset when using NSKeyedArchiver on UserDefaults

I have an array of SKSpriteNode objects that I want to persist to UserDefaults .我有一组SKSpriteNode对象,我想将它们保留到UserDefaults In the code below ( or see demo project on GitHub ), I use NSKeyedUnarchiver to encode the array as data before setting it to defaults.在下面的代码中(或查看 GitHub 上的演示项目),我使用NSKeyedUnarchiver将数组编码为数据,然后再将其设置为默认值。 But when I unarchive the data, the engineSize property of the objects is reset to the default value of 0.但是当我engineSize数据时,对象的engineSize属性被重置为默认值 0。

Car.swift Car.swift

import SpriteKit

class Car: SKSpriteNode {
    var engineSize: Int = 0

    init() {
        super.init(texture: nil, color: .blue, size: CGSize(width: 100, height: 100))
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
}

GameScene.swift GameScene.swift

import SpriteKit

class GameScene: SKScene {
    let defaults = UserDefaults.standard
    var carArray = [Car]()

    override func didMove(to view: SKView) {
        for _ in 1...3 {
            let car = Car()
            car.engineSize = 2000
            carArray.append(car)
        }

        // Save to defaults
        defaults.set(NSKeyedArchiver.archivedData(withRootObject: carArray), forKey: "carArrayKey")

        // Restore from defaults
        let arrayData = defaults.data(forKey: "carArrayKey")
        carArray = NSKeyedUnarchiver.unarchiveObject(with: arrayData!) as! [Car]
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        for c in carArray {
            print("Car's engine size is \(c.engineSize)")
        }
    }
}

This answer led me to try implementing encoder/decoder methods on the Car class:这个答案让我尝试在Car类上实现编码器/解码器方法:

Car.swift (updated) Car.swift(更新)

import SpriteKit

class Car: SKSpriteNode {
    var engineSize: Int = 0

    init() {
        super.init(texture: nil, color: .blue, size: CGSize(width: 100, height: 100))
    }

    required convenience public init(coder decoder: NSCoder) {
        self.init()

        if let engineSize = decoder.decodeObject(forKey: "engineSize") as? Int {
            self.engineSize = engineSize
        }
    }

    func encodeWithCoder(coder : NSCoder) {
        coder.encode(self.engineSize, forKey: "engineSize")
    }
}

However, this doesn't seem to be working.但是,这似乎不起作用。 GameScene is still printing the engineSize as 0. Am I implementing the coder/decoder wrong? GameScene仍然将engineSize打印为 0。我是否错误地实现了编码器/解码器? What can I do to prevent the engineSize properties from resetting to 0 when they are restored from defaults?当它们从默认值恢复时,我可以做些什么来防止engineSize属性重置为 0?

UPDATE更新

Here is my updated Car class as I'm trying to get it to work with rmaddy's suggestions:这是我更新的Car类,因为我试图让它与 rmaddy 的建议一起工作:

import SpriteKit

class Car: SKSpriteNode {
    var engineSize: Int = 0

    init() {
        super.init(texture: nil, color: .blue, size: CGSize(width: 100, height: 100))
    }

    required init?(coder decoder: NSCoder) {
        super.init(coder: decoder)
        if let engineSize = decoder.decodeObject(forKey: "engineSize") as? Int {
            self.engineSize = engineSize
        }
    }

    func encodeWithCoder(coder : NSCoder) {
        super.encode(with: coder)
        coder.encode(self.engineSize, forKey: "engineSize")
    }
}

The code compiles and runs but the engineSize property is still being reset to 0 when saving to defaults.代码编译并运行,但在保存为默认值时, engineSize属性仍被重置为 0。

I managed to get this working.我设法让这个工作。 Here are the updated GameScene and Car code files with NSCoding properly implemented.以下是正确实现NSCoding的更新后的GameSceneCar代码文件。

Car.swift Car.swift

import SpriteKit

class Car: SKSpriteNode {
    var engineSize: Int = 0
    
    init() {
        super.init(texture: nil, color: .blue, size: CGSize(width: 100, height: 100))
    }
    
    required init(coder aDecoder: NSCoder) {
        engineSize = aDecoder.decodeInteger(forKey: "engineSize")
        super.init(texture: nil, color: .blue, size: CGSize(width: 100, height: 100))
    }
    
    override func encode(with aCoder: NSCoder) {
        aCoder.encode(engineSize, forKey: "engineSize")
    }
}

GameScene.swift GameScene.swift

import SpriteKit

class GameScene: SKScene {
    let defaults = UserDefaults.standard
    var carArray = [Car]()
    
    override func didMove(to view: SKView) {
        for _ in 1...3 {
            let car = Car()
            car.engineSize = 2000
            carArray.append(car)
        }
        
        defaults.set(NSKeyedArchiver.archivedData(withRootObject: carArray), forKey: "carArrayKey")
        
        let arrayData = defaults.data(forKey: "carArrayKey")
        carArray = NSKeyedUnarchiver.unarchiveObject(with: arrayData!) as! [Car]
    }
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        for c in carArray {
            print("Car's engine size is \(c.engineSize)")
        }
    }
}

UPDATE更新

If you implement init(coder aDecoder: NSCoder) and encode(with aCoder: NSCoder) as shown above, you may notice that some type properties like color , position , or size are being reset to their initial values after you archive/unarchive them.如果您实现init(coder aDecoder: NSCoder)encode(with aCoder: NSCoder)如上所示,您可能会注意到一些类型属性,如colorpositionsize在您存档/取消存档后被重置为它们的初始值。 This is because you are now providing your own implementation of these two methods, so you can no longer rely on the superclass to handle the encoding/decoding of the type properties for you.这是因为您现在提供了这两个方法的自己的实现,因此您不能再依赖超类来为您处理类型属性的编码/解码。

You must now implement the encoding/decoding for all properties that you care about, both your own custom properties and the type's properties.您现在必须为您关心的所有属性实现编码/解码,包括您自己的自定义属性和类型的属性。 For example:例如:

required init(coder aDecoder: NSCoder) {
    super.init(texture: nil, color: .blue, size: CGSize(width: 100, height: 100))
    engineSize = aDecoder.decodeInteger(forKey: "engineSize")
    self.position = aDecoder.decodeCGPoint(forKey: "position")
    self.alpha = aDecoder.decodeObject(forKey: "alpha") as! CGFloat
    self.zPosition = aDecoder.decodeObject(forKey: "zPosition") as! CGFloat
    self.name = aDecoder.decodeObject(forKey: "name") as! String?
    self.colorBlendFactor = aDecoder.decodeObject(forKey: "colorBlendFactor") as! CGFloat
    self.color = aDecoder.decodeObject(forKey: "color") as! UIColor
    self.size = aDecoder.decodeCGSize(forKey: "size")
    self.texture = aDecoder.decodeObject(forKey: "texture") as! SKTexture?
}

override func encode(with aCoder: NSCoder) {
    aCoder.encode(engineSize, forKey: "engineSize")
    aCoder.encode(self.position, forKey: "position")
    aCoder.encode(self.alpha, forKey: "alpha")
    aCoder.encode(self.zPosition, forKey: "zPosition")
    aCoder.encode(self.name, forKey: "name")
    aCoder.encode(self.colorBlendFactor, forKey: "colorBlendFactor")
    aCoder.encode(self.color, forKey: "color")
    aCoder.encode(self.size, forKey: "size")
    aCoder.encode(self.texture, forKey: "texture")
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM