在UserDefaults上使用NSKeyedArchiver时重置属性

时间:2018-01-11 02:16:57

标签: ios swift sprite-kit nskeyedarchiver userdefaults

我有一组SKSpriteNode个对象,我希望将其保存到UserDefaults。在下面的代码(or see demo project on GitHub)中,我使用NSKeyedUnarchiver将数组编码为数据,然后再将其设置为默认值。但是当我取消归档数据时,对象的engineSize属性将重置为默认值0。

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

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让我尝试在Car类上实现编码器/解码器方法:

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")
    }
}

然而,这似乎并没有起作用。 GameScene仍然将engineSize打印为0.我是否实现了编码器/解码器错误?从默认值恢复engineSize属性时,如何防止Car属性重置为0?

更新

以下是我更新的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") } } 课程,因为我试图让它与rmaddy的建议一起使用:

engineSize

代码编译并运行,但保存为默认值时,if request.method == 'POST': form = RegisterForm(request.POST) if form.is_vaild(): .... else: # don't new form print(form.errors.as_text()) return render(request, 'users/register.html', {'register': form}) 属性仍然被重置为0。

1 个答案:

答案 0 :(得分:-1)

在与Big Nerd Ranch iOS书籍共度美好时光之后,我设法让这个有用了。以下是已正确实施GameScene的已更新CarNSCoding个代码文件。

<强> 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

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)")
        }
    }
}

<强>更新

如果您如上所示实施init(coder aDecoder: NSCoder)encode(with aCoder: NSCoder),您可能会注意到正在重置某些类型的属性,例如colorpositionsize归档/取消归档后的初始值。这是因为您现在提供了这两种方法的自己的实现,因此您不再依赖超类来处理类型属性的编码/解码。

您现在必须为您关注的所有属性实现编码/解码,包括您自己的自定义属性和类型属性。例如:

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")
}