微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

NSKeyedUnarchiver.unarchiveTopLevelObjectWithData返回nil

如何解决NSKeyedUnarchiver.unarchiveTopLevelObjectWithData返回nil

我想将对象数组保存到UserDefaults中,然后重新加载。

当尝试取消归档数据时,它总是返回nil ..有什么想法吗?

这是我的对象

class DbInsideLevel: NSObject,NSSecureCoding {
    
    static var supportsSecureCoding: Bool {
        return true
    }
    
    let idNum: Int!
    var topicId: Int = 0
    var tryCount: Int = 0
    var score: Int = 0
    var isOpen: Bool = false
    var lastPlayedDate: Date?
    
    init(idNum: Int,topicId: Int,tryCount: Int = 0,score: Int = 0,open: Bool,lastPlayedDate: Date?) {
        self.idNum = idNum
        self.topicId = topicId
        self.tryCount = tryCount
        self.score = score
        self.isOpen = open
        self.lastPlayedDate = lastPlayedDate
    }
    
    convenience required init?(coder: NSCoder) {
        
        guard
            let idNum       = coder.decodeObject(forKey: "idNum") as? Int,let topicId     = coder.decodeObject(forKey: "topicId") as? Int,let tryCount    = coder.decodeObject(forKey: "tryCount") as? Int,let score       = coder.decodeObject(forKey: "score") as? Int,let open        = coder.decodeObject(forKey: "isOpen") as? Bool,let lastPlayed  = coder.decodeObject(forKey: "lastPlayedDate") as? Date
        else {
            return nil
        }
        
        self.init(idNum: idNum,topicId: topicId,tryCount: tryCount,score: score,open: open,lastPlayedDate: lastPlayed)
    }
    
    func encode(with coder: NSCoder) {
        coder.encode(idNum,forKey: "idNum")
        coder.encode(topicId,forKey: "topicId")
        coder.encode(tryCount,forKey: "tryCount")
        coder.encode(score,forKey: "score")
        coder.encode(isOpen,forKey: "isOpen")
        coder.encode(lastPlayedDate,forKey: "lastPlayedDate")
    }

    func update(score: Int) {
        
        //  Update score if necessary
        if score > self.score {
            self.score = score
        }
        
        //  Increment try count
        self.tryCount = self.tryCount + 1
    }
}

归档数据:

func archiveData(with object: Any,to key: String) -> Data? {
    do {
        guard
            let data = try? NSKeyedArchiver.archivedData(withRootObject: object,requiringSecureCoding: true) else {
            return nil
        }
        
        return data
    }
}

取消存档数据:

func unarchiveData(data: Data) -> Any? {
    
    do {
        let unarchivedData = try NSKeyedUnarchiver.unarchivetopLevelObjectWithData(data) as? [DbInsideLevel]
        return unarchivedData
    } catch {
        return nil
    }
}

保存为用户认设置:

class func americanSaveContext(data: [DbInsideLevel]) {
    if data.count != 0 {
        if let archived = MainDb.sharedInstance.archiveData(with: data,to: AppConstants.Keys.UserDefaults.American) {
            MainDb.sharedInstance.dataStore(data: archived)
        }
    }
}

从UserDefaults加载:

class func americanDataRetrive(topicId: Int) -> [DbInsideLevel]? {
    if let data = MainDb.sharedInstance.dataRetrive() {
        let unarchived = MainDb.sharedInstance.unarchiveData(data: data) as! [DbInsideLevel]
        return unarchived
    }
    
    return nil
}

用于保存/加载的UserDefaults助手:

extension MainDb {
    
    //  MARK: - UserDefaults helpers
    
    func dataRetrive() -> Data? {
        let defaults = UserDefaults.standard
        return defaults.value(forKey: AppConstants.Keys.UserDefaults.American) as? Data
    }
    
    func dataStore(data: Data) {
        let defaults = UserDefaults.standard
        defaults.setValue(data,forKey: AppConstants.Keys.UserDefaults.American)
    }
    
    func dataReset() {
        let defaults = UserDefaults.standard
        defaults.removeObject(forKey: AppConstants.Keys.UserDefaults.American)
    }
}

解决方法

您的问题是,您使用错误的方法来解码整数和布尔值。我还将实现所需的解码器,而不是便捷的初始化程序。

required init(coder decoder: NSCoder) {
    self.idNum = decoder.decodeInteger(forKey: "idNum")
    self.topicId = decoder.decodeInteger(forKey: "topicId")
    self.tryCount = decoder.decodeInteger(forKey: "tryCount")
    self.score = decoder.decodeInteger(forKey: "score")
    self.isOpen = decoder.decodeBool(forKey: "isOpen")
    self.lastPlayedDate = decoder.decodeObject(forKey: "lastPlayedDate") as? Date
}

请注意,您还可以使用Swift 4或更高版本的Codable协议对自定义类/结构进行编码和解码,并将它们另存为UserDefaults中的数据或应用程序支持文件夹中的plist文件。最后但并非最不重要的一点是,不要使用setValue和value(forKey :)来保存和检索数据,用户默认值具有一种用于检索数据的特定方法,称为data(forKey:)set(_ value: Any)以保留数据:>

extension UserDefaults {
    func decode<T: Decodable>(_ type: T.Type,forKey defaultName: String) throws -> T {
        try JSONDecoder().decode(T.self,from: data(forKey: defaultName) ?? .init())
    }
    func encode<T: Encodable>(_ value: T,forKey defaultName: String) throws {
        try set(JSONEncoder().encode(value),forKey: defaultName)
    }
}

class DbInsideLevel: Codable {
    let idNum: Int!
    var topicId: Int = 0
    var tryCount: Int = 0
    var score: Int = 0
    var isOpen: Bool = false
    var lastPlayedDate: Date?
    init(idNum: Int,topicId: Int,tryCount: Int = 0,score: Int = 0,open: Bool,lastPlayedDate: Date?) {
        self.idNum = idNum
        self.topicId = topicId
        self.tryCount = tryCount
        self.score = score
        self.isOpen = open
        self.lastPlayedDate = lastPlayedDate
    }
    func update(score: Int) {
        if score > self.score {
            self.score = score
        }
        self.tryCount = self.tryCount + 1
    }
}

游乐场测试

let insideLevel = DbInsideLevel(idNum: 1,topicId: 2,tryCount: 3,score: 4,open: true,lastPlayedDate: Date())
do {
    try UserDefaults.standard.encode(insideLevel,forKey: "insideLevel")
    let decodedLevel = try UserDefaults.standard.decode(DbInsideLevel.self,forKey: "insideLevel")
    print("decodedLevel idNum",decodedLevel.idNum) // decodedLevel idNum Optional(1)
} catch {
    print(error)
}

编辑/更新:

它也适用于Codable类型的数组:

let insideLevel = DbInsideLevel(idNum: 1,lastPlayedDate: Date())
do {
    try UserDefaults.standard.encode([insideLevel],forKey: "insideLevels")
    let decodedLevel = try UserDefaults.standard.decode([DbInsideLevel].self,forKey: "insideLevels")
    print("decodedLevel idNum",decodedLevel.first?.idNum) // decodedLevel idNum Optional(1)
} catch {
    print(error)
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。