首页 > 解决方案 > 使用 NSKeyedArchiver 保存枚举和嵌套数组

问题描述

作为初学者,我发现持久数据存储是一项艰巨的挑战。在使用 Core Data 和 SQLite 并没有取得多大成功之后,我终于找到了一个使用 NSKeyedArchiver 的好例子。但现在我需要扩展它以包括枚举和嵌套数组。

首先,我尝试仅添加枚举,但在保存过程中失败。

在下面的代码中,我还尝试添加嵌套数组,但这会使 IDE 崩溃。

没有颜色枚举和 STARS 嵌套数组的简化版本工作得很好。

//
//  ViewController.swift
//

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.

        //Create a test object
        let myPlanets1 = [PlanetStruct(name: "Earth", isHabitable: true), PlanetStruct(name: "Mars", isHabitable: false)]
        let myStar1 = StarStruct(name: "Sun", PLANETS: myPlanets1)
        let myPlanets2 = [PlanetStruct(name: "Herculis", isHabitable: true), PlanetStruct(name: "Delphini", isHabitable: false), PlanetStruct(name: "Librae", isHabitable: false)]
        let myStar2 = StarStruct(name: "Rigel", PLANETS: myPlanets2)
        let myStorage = StorageUnit(heading: "test", items: ["Arthur", "Ford", "Trillian", "Zaphod", "Marvin"], numbers: [[1, 2, 3],[4, 5, 6]], color: ColorEnum.red, stars: [myStar1, myStar2])
        print("Created: \(String(describing: myStorage.heading))")

        //Where to save?
        let DocumentsDirectory = FileManager().urls(for: .documentDirectory, in: .userDomainMask).first!
        let ArchiveURL = DocumentsDirectory.appendingPathComponent("storageunit")

        //Save test object
        do {
            let data = try NSKeyedArchiver.archivedData(withRootObject: myStorage, requiringSecureCoding: false)
            try data.write(to: ArchiveURL)
        } catch {
            print("Couldn't write file")
        }

        //Load data into a new object
        let codedData = try? Data(contentsOf: ArchiveURL)
        var RestoredStorage = StorageUnit(heading: "", items: [], numbers: [[]], color: ColorEnum.red, stars: [])

        do {
            if let loadedObject = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(codedData!) as? StorageUnit {
                RestoredStorage = loadedObject
            }
        } catch {
            print("Couldn't read file.")
        }

        //Print loaded data for verification
        print("Loaded: \(String(describing: RestoredStorage.heading))")

    }
}


struct PlanetStruct {
    var name : String
    var isHabitable : Bool
}

struct StarStruct {
    var name : String
    var PLANETS : [PlanetStruct]
}

enum ColorEnum : Int { case red, yellow, blue }


class StorageUnit: NSObject, NSCoding {

    var heading : String
    var items : [String]
    var numbers: [[Int]]
    var color : ColorEnum
    var STARS : [StarStruct]


    struct PropertyKey {
        static let headingKey = "heading"
        static let itemsKey = "items"
        static let numbersKey = "numbers"
        static let colorKey = "color"
        static let starKey = "stars"
    }

    required convenience init(coder aDecoder: NSCoder) {
        let heading = aDecoder.decodeObject(forKey: PropertyKey.headingKey) as! String
        let items = aDecoder.decodeObject(forKey: PropertyKey.itemsKey) as! [String]
        let numbers = aDecoder.decodeObject(forKey: PropertyKey.numbersKey) as! [[Int]]
        let stars = aDecoder.decodeObject(forKey: PropertyKey.starKey) as! [StarStruct]
        let color = aDecoder.decodeObject(forKey: PropertyKey.colorKey) as! ColorEnum
        self.init(heading: heading, items: items, numbers: numbers, color: color, stars: stars)
    }

    init(heading: String, items : [String], numbers: [[Int]],color: ColorEnum, stars: [StarStruct]) {
        self.heading = heading
        self.items = items
        self.numbers = numbers
        self.color = color
        self.STARS = stars

        super.init()
    }

    func encode(with aCoder: NSCoder) {
        aCoder.encode(heading, forKey: PropertyKey.headingKey)
        aCoder.encode(items, forKey: PropertyKey.itemsKey)
        aCoder.encode(numbers, forKey: PropertyKey.numbersKey)
        aCoder.encode(color, forKey: PropertyKey.colorKey)
        aCoder.encode(STARS, forKey: PropertyKey.starKey)
    }
}

标签: xcodeswift5

解决方案


感谢 Codable 的提示。

这篇文章对我很有用

与往常一样,我发现的示例与 Swift5 不直接兼容......

但是下面的这段代码对我有用:-) 不过可能需要在错误处理上做一些工作。

//
//  ViewController.swift
//

import UIKit

class ViewController: UIViewController {

    var MYDATA = DataStruct(owner: "", STARS: [])

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.

        // Make some test data
        let PLANETS1 = [PlanetStruct(name: "Earth", isHabitable: true), PlanetStruct(name: "Mars", isHabitable: false)]
        let PLANETS2 = [PlanetStruct(name: "Herculis", isHabitable: true), PlanetStruct(name: "Delphini", isHabitable: false), PlanetStruct(name: "Librae", isHabitable: false)]

        let STAR1 = StarStruct(name: "Sun", PLANETS: PLANETS1, color: ColorEnum.red)
        let STAR2 = StarStruct(name: "Rigel", PLANETS: PLANETS2, color: ColorEnum.blue)

        let MYDATA = DataStruct(owner: "Bob", STARS: [STAR1, STAR2])


        // Where to save?
        let DocumentsDirectory = FileManager().urls(for: .documentDirectory, in: .userDomainMask).first!
        let ArchiveURL = DocumentsDirectory.appendingPathComponent("mydata")


        // Save data
        SaveData(myDataStruct: MYDATA, FileURL: ArchiveURL)
        print("Saved data: \(MYDATA.owner)")


        // Load data
        let LOADED = LoadData(FileURL: ArchiveURL)
        print("Loaded data:  \(LOADED?.owner ?? "Failed")" )
        print("Loaded data:  \(LOADED?.STARS[1].PLANETS[0].name ?? "Failed")" )

    }



    func SaveData(myDataStruct: DataStruct, FileURL : URL) {
        do {
            let propertylist = try PropertyListEncoder().encode(myDataStruct)
            let data = try NSKeyedArchiver.archivedData(withRootObject: propertylist, requiringSecureCoding: false )
            try data.write(to: FileURL)
        } catch {
            print("Save failed")
        }
    }


    func LoadData(FileURL : URL ) -> DataStruct? {
        if let data = try? Data(contentsOf: FileURL) {
            do {
                let propertylist = (try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? Data)!
                let loadedData = try PropertyListDecoder().decode(DataStruct.self, from: propertylist)
                return loadedData
            } catch {
                print("Load failed")
            }
        }

        //Return empty
        return nil
    }
}






enum ColorEnum : Int, Codable { case red, yellow, blue }

struct PlanetStruct : Codable {
    var name : String = ""
    var isHabitable : Bool = false
}

struct StarStruct : Codable {
    var name : String = ""
    var PLANETS : [PlanetStruct]
    var color : ColorEnum
}

struct DataStruct : Codable {
    var owner : String = ""
    var STARS : [StarStruct]
}


推荐阅读