NSKeyedUnarchiver.unarchiveTopLevelObjectWithData return 无
NSKeyedUnarchiver.unarchiveTopLevelObjectWithData return nil
我想将一组对象保存到 UserDefaults 中并加载回来。
尝试取消归档数据时,它总是 returns 没有..有什么想法吗?
这是我的对象:
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 协议对您的自定义 classes/structures 进行编码和解码,并将它们保存为 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)
}
edit/update:
它也适用于 Codable 类型的数组:
let insideLevel = DbInsideLevel(idNum: 1, topicId: 2, tryCount: 3, score: 4, open: true, 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)
}
我想将一组对象保存到 UserDefaults 中并加载回来。
尝试取消归档数据时,它总是 returns 没有..有什么想法吗?
这是我的对象:
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 协议对您的自定义 classes/structures 进行编码和解码,并将它们保存为 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)
}
edit/update:
它也适用于 Codable 类型的数组:
let insideLevel = DbInsideLevel(idNum: 1, topicId: 2, tryCount: 3, score: 4, open: true, 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)
}