使用NSKeyedArchiver和Userdefault保存符合NSCoding协议的对象的结构数组

7
作为一个面向协议编程的概念,我使用Struct创建了我的模型。
我想把"Struct"的数组保存到Userdefault中。但是,在对该模型的数组进行编码/解码时,我遇到了问题。
这是我的模型结构
struct Room {
    let name : String
    let id : String
    let booked : Bool
}

我创建了一个像这样的扩展

extension Room {


func decode() -> Room? {
    let userClassObject = NSKeyedUnarchiver.unarchiveObject(withFile: RoomClass.path()) as? RoomClass
    return userClassObject?.room
}

func encode() {
    let personClassObject = RoomClass(room: self)
    NSKeyedArchiver.archiveRootObject(personClassObject, toFile: RoomClass.path())
}

class RoomClass: NSObject, NSCoding {

    var room : Room?

    init(room: Room) {
        self.room = room
        super.init()
    }

    class func path() -> String {
        let documentsPath = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).first
        let path = documentsPath?.appending(("/Room"))
        return path!
    }

    func encode(with aCoder: NSCoder) {
        aCoder.encode(room!.name, forKey: "name")
        aCoder.encode(room!.id, forKey: "Id")
        aCoder.encode(room!.booked, forKey: "booked")
    }

    required init?(coder aDecoder: NSCoder) {
        let _name = aDecoder.decodeObject(forKey: "name") as? String
        let _id = aDecoder.decodeObject(forKey: "Id") as? String
        let _booked = aDecoder.decodeBool(forKey: "booked")

        room = Room(name: _name!, id: _id!, booked: _booked)

        super.init()
    }
}
}

当我尝试像这样保存arrRoomList(一组房间对象的数组)时:
        self.saveRooms(arrayRooms: arrRoomList)

我遇到了这个错误

[_SwiftValue encodeWithCoder:]: unrecognized selector sent to instance

我也尝试过先对每个对象进行编码,然后再尝试将它们保存在默认设置中,但仍然出现错误。

请问有人可以指导我如何正确地对用户默认设置中的结构体数组进行编码/解码,而不必将其转换为字典吗?


看起来很复杂,你是将结构体转换为类进行编码和保存吗? - Tj3n
1
@Idan:我已经修改了我的问题并提供了更多细节。我想将符合NSCoding的对象保存到默认值中。 - Wolverine
不要使用NSCoding之类的东西,只需将您的对象转换为字典,然后保存即可,非常简单,代码量也相似。 - Tj3n
你为什么认为这不好?基本上它就是 NSCoding 所做的事情,但在所有操作完成后只是将它们编码为 NSData。 - Tj3n
@Tj3n 也许你是对的。同时NSCoding被用于面向对象设计模式。让我们看看是否有任何官方持久化方法引入了Swift与面向协议编程概念。 - Wolverine
显示剩余4条评论
2个回答

11

你可以设置结构体直接使用NSKeyedArchiver,方法如下:

struct Room {
    let name : String
    let id : String
    let booked : Bool
}

extension Room {
    func encode() -> Data {
        let data = NSMutableData()
        let archiver = NSKeyedArchiver(forWritingWith: data)
        archiver.encode(name, forKey: "name")
        archiver.encode(id, forKey: "id")
        archiver.encode(booked, forKey: "booked")
        archiver.finishEncoding()
        return data as Data
    }

    init?(data: Data) {
        let unarchiver = NSKeyedUnarchiver(forReadingWith: data)
        defer {
            unarchiver.finishDecoding()
        }
        guard let name = unarchiver.decodeObject(forKey: "name") as? String else { return nil }
        guard let id = unarchiver.decodeObject(forKey: "id") as? String else { return nil }
        booked = unarchiver.decodeBool(forKey: "booked")
        self.name = name
        self.id = id
    }
}

要与UserDefaults一起使用,请像这样调用:

// to encode to data and save to user defaults
let room = Room(name: "asdf", id: "123", booked: true)
UserDefaults.standard.set(room.encode(), forKey: "room")

// to retrieve from user defaults
if let data = UserDefaults.standard.object(forKey: "room") as? Data {
    let room = Room(data: data)
}

可以像这样保存/检索房间数组:

func saveRooms(arrayRooms: [Room]) {
    let roomsData = arrayRooms.map { $0.encode() }
    UserDefaults.standard.set(roomsData, forKey: "rooms")
}

func getRooms() -> [Room]? {
    guard let roomsData = UserDefaults.standard.object(forKey: "rooms") as? [Data] else { return nil }
    return roomsData.flatMap { return Room(data: $0) }
}


// save 2 rooms to user defaults
let roomA = Room(name: "A", id: "123", booked: true)
let roomB = Room(name: "B", id: "asdf", booked: false)
saveRooms(arrayRooms: [roomA, roomB])

// get the rooms
print(getRooms())

1
您可以尝试使用模型,如下所示:

Model

class CardModel: NSObject
{
    let name : String
    let id : String
    let booked : Bool

    override init()
    {
        self.name = ""
        self.id = ""
        self.booked = false
    }

    required init(coder aDecoder: NSCoder)
    {
        self.name = aDecoder.decodeObject(forKey: "name") as! String
        self.id = aDecoder.decodeObject(forKey: "id") as! String
        self.booked = aDecoder.decodeObject(forKey: "booked") as! Bool
     }

    func encodeWithCoder(_ aCoder: NSCoder)
    {
        aCoder.encode(name, forKey: "name")
        aCoder.encode(id, forKey: "id")
        aCoder.encode(booked, forKey: "booked")
    }
}

使用方法:创建CardModel模型对象。
let objCardModel = CardModel()
objCardModel.name = "Shrikant"
objCardModel.id = "8"
objCardModel.booked = true

通过对象访问

let userName = objCardModel.name

1
是的,但作为面向协议编程的概念,我已经使用了 Struct 创建了我的模型。我知道我可以只创建类来实现这一点。有没有办法在 Struct 中实现这个呢? - Wolverine
默认对象必须是属性列表,也就是NSData、NSString、NSNumber、NSDate、NSArray或NSDictionary的实例(对于集合,可以是这些类型的组合)。如果您想存储其他类型的对象,通常应将其归档以创建NSData的实例。 - Shrikant Tanwade
1
这些数据的存储和检索在哪里以及如何进行? - Confused

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接