尝试在 Swift 3 中保存自定义对象时尝试插入非属性列表对象
Posted
技术标签:
【中文标题】尝试在 Swift 3 中保存自定义对象时尝试插入非属性列表对象【英文标题】:Attempt to insert non-property list object when trying to save a custom object in Swift 3 【发布时间】:2022-01-09 07:08:16 【问题描述】:我有一个符合NSCoding
协议的简单对象。
import Foundation
class JobCategory: NSObject, NSCoding
var id: Int
var name: String
var URLString: String
init(id: Int, name: String, URLString: String)
self.id = id
self.name = name
self.URLString = URLString
// MARK: - NSCoding
required init(coder aDecoder: NSCoder)
id = aDecoder.decodeObject(forKey: "id") as? Int ?? aDecoder.decodeInteger(forKey: "id")
name = aDecoder.decodeObject(forKey: "name") as! String
URLString = aDecoder.decodeObject(forKey: "URLString") as! String
func encode(with aCoder: NSCoder)
aCoder.encode(id, forKey: "id")
aCoder.encode(name, forKey: "name")
aCoder.encode(URLString, forKey: "URLString")
我正在尝试将它的一个实例保存在 UserDefaults
中,但它一直失败并出现以下错误。
由于未捕获的异常“NSInvalidArgumentException”而终止应用程序,原因:“尝试为关键 jobCategory 插入非属性列表对象”
这是我保存在UserDefaults
中的代码。
enum UserDefaultsKeys: String
case jobCategory
class ViewController: UIViewController
@IBAction func didTapSaveButton(_ sender: UIButton)
let category = JobCategory(id: 1, name: "Test Category", URLString: "http://www.example-job.com")
let userDefaults = UserDefaults.standard
userDefaults.set(category, forKey: UserDefaultsKeys.jobCategory.rawValue)
userDefaults.synchronize()
我将枚举值替换为普通字符串,但仍然出现相同的错误。知道是什么原因造成的吗?
【问题讨论】:
我创建了一个类似的问题,询问这是在哪里记录的(在线或在操作指南中)。你找到这方面的资源了吗? userDefaults.synchronize 是不必要的,不应使用。 【参考方案1】:您需要使用archivedData(withRootObject:)
从您的JobCategory
实例创建Data
实例,并将该Data
实例存储在UserDefaults
中,然后使用unarchiveTopLevelObjectWithData(_:)
取消归档,所以试试这样。
用于将数据存储在UserDefaults
let category = JobCategory(id: 1, name: "Test Category", URLString: "http://www.example-job.com")
let encodedData = NSKeyedArchiver.archivedData(withRootObject: category, requiringSecureCoding: false)
let userDefaults = UserDefaults.standard
userDefaults.set(encodedData, forKey: UserDefaultsKeys.jobCategory.rawValue)
用于从UserDefaults
检索数据
let decoded = UserDefaults.standard.object(forKey: UserDefaultsKeys.jobCategory.rawValue) as! Data
let decodedTeams = NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(decoded) as! JobCategory
print(decodedTeams.name)
【讨论】:
我正在尝试在 NSArchiveObject 上执行此操作,但会导致错误 我创建了一个类似的问题,询问这是在哪里记录的(在线或在操作指南中)。你找到这方面的资源了吗? NSKeyedArchiver 可能会引发错误,因此应将其标记为“尝试”并同时处理错误。 我正在尝试存储方法,我收到以下错误:数据无法写入,因为它的格式不正确。【参考方案2】:更新 Swift 4、Xcode 10
我已经围绕它编写了一个结构以便于访问。
//set, get & remove User own profile in cache
struct UserProfileCache
static let key = "userProfileCache"
static func save(_ value: Profile!)
UserDefaults.standard.set(try? PropertyListEncoder().encode(value), forKey: key)
static func get() -> Profile!
var userData: Profile!
if let data = UserDefaults.standard.value(forKey: key) as? Data
userData = try? PropertyListDecoder().decode(Profile.self, from: data)
return userData!
else
return userData
static func remove()
UserDefaults.standard.removeObject(forKey: key)
Profile 是一个 Json 编码的对象。
struct Profile: Codable
let id: Int!
let firstName: String
let dob: String!
用法:
//save details in user defaults...
UserProfileCache.save(profileDetails)
希望对你有帮助!!!
谢谢
【讨论】:
【参考方案3】:将字典保存到用户默认中
let data = NSKeyedArchiver.archivedData(withRootObject: DictionaryData)
UserDefaults.standard.set(data, forKey: kUserData)
检索字典
let outData = UserDefaults.standard.data(forKey: kUserData)
let dict = NSKeyedUnarchiver.unarchiveObject(with: outData!) as! NSDictionary
【讨论】:
我创建了一个类似的问题,询问这是在哪里记录的(在线或在操作指南中)。你找到这方面的资源了吗?【参考方案4】:Swift 将 Codable
对象保存到 UserDefault
和 @propertyWrapper
@propertyWrapper
struct UserDefault<T: Codable>
let key: String
let defaultValue: T
init(_ key: String, defaultValue: T)
self.key = key
self.defaultValue = defaultValue
var wrappedValue: T
get
if let data = UserDefaults.standard.object(forKey: key) as? Data,
let user = try? JSONDecoder().decode(T.self, from: data)
return user
return defaultValue
set
if let encoded = try? JSONEncoder().encode(newValue)
UserDefaults.standard.set(encoded, forKey: key)
enum GlobalSettings
@UserDefault("user", defaultValue: User(name:"",pass:"")) static var user: User
示例用户模型确认可编码
struct User:Codable
let name:String
let pass:String
如何使用
//Set value
GlobalSettings.user = User(name: "Ahmed", pass: "Ahmed")
//GetValue
print(GlobalSettings.user)
【讨论】:
这看起来像一些巫毒魔法✨但我喜欢它! 有人在玩属性包装器,哈哈。干杯。【参考方案5】:基于Harjot Singh 的回答。我是这样用的:
struct AppData
static var myObject: MyObject?
get
if UserDefaults.standard.object(forKey: "UserLocationKey") != nil
if let data = UserDefaults.standard.value(forKey: "UserLocationKey") as? Data
let myObject = try? PropertyListDecoder().decode(MyObject.self, from: data)
return myObject!
return nil
set
UserDefaults.standard.set(try? PropertyListEncoder().encode(newValue), forKey: "UserLocationKey")
【讨论】:
【参考方案6】:这是一个 UserDefaults
扩展,用于设置和获取 Codable
对象,如果您将其作为纯文本文件打开,则在 plist(用户默认值)中保持人类可读:
extension Encodable
var asDictionary: [String: Any]?
guard let data = try? JSONEncoder().encode(self) else return nil
return try? JSONSerialization.jsonObject(with: data) as? [String : Any]
extension Decodable
init?(dictionary: [String: Any])
guard let data = try? JSONSerialization.data(withJSONObject: dictionary) else return nil
guard let object = try? JSONDecoder().decode(Self.self, from: data) else return nil
self = object
extension UserDefaults
func setEncodableAsDictionary<T: Encodable>(_ encodable: T, for key: String)
self.set(encodable.asDictionary, forKey: key)
func getDecodableFromDictionary<T: Decodable>(for key: String) -> T?
guard let dictionary = self.dictionary(forKey: key) else
return nil
return T(dictionary: dictionary)
如果您还想支持(可编码的)数组与 plist 数组之间的往来,请将以下内容添加到扩展中:
extension UserDefaults
func setEncodablesAsArrayOfDictionaries<T: Encodable>(_ encodables: Array<T>, for key: String)
let arrayOfDictionaries = encodables.map( $0.asDictionary )
self.set(arrayOfDictionaries, forKey: key)
func getDecodablesFromArrayOfDictionaries<T: Decodable>(for key: String) -> [T]?
guard let arrayOfDictionaries = self.array(forKey: key) as? [[String: Any]] else
return nil
return arrayOfDictionaries.compactMap( T(dictionary: $0) )
如果您不关心 plist 是否可读,可以简单地将其保存为 Data
(如果以纯文本形式打开,则看起来像随机字符串):
extension UserDefaults
func setEncodable<T: Encodable>(_ encodable: T, for key: String) throws
let data = try PropertyListEncoder().encode(encodable)
self.set(data, forKey: key)
func getDecodable<T: Decodable>(for key: String) -> T?
guard
self.object(forKey: key) != nil,
let data = self.value(forKey: key) as? Data
else
return nil
let obj = try? PropertyListDecoder().decode(T.self, from: data)
return obj
(使用第二种方法,您不需要顶部的 Encodable
和 Decodable
扩展)
【讨论】:
以上是关于尝试在 Swift 3 中保存自定义对象时尝试插入非属性列表对象的主要内容,如果未能解决你的问题,请参考以下文章
如何在Swift 3中修复我的自定义UITextField对象?
目标 C:我正在尝试保存一个包含原语和对象的 NSDictionary,但是在我尝试添加自定义对象时发生错误
尝试使用 NSKeyedArchiver 在 UserDefaults 中保存自定义对象
如何在 Swift 中归档和取消归档自定义对象?或者如何在 Swift 中将自定义对象保存到 NSUserDefaults?