Ios 尝试将非属性列表对象设置为NSUserDefaults
我以为我知道是什么导致了这个错误,但我似乎无法找出我做错了什么 以下是我收到的完整错误消息: Attempt to set a non-property-list object ( "<BC_Person: 0x8f3c140>" ) as an NSUserDefaults value for key personDataArray 在应用程序中的某个点上,设置了Ios 尝试将非属性列表对象设置为NSUserDefaults,ios,objective-c,encoding,nsuserdefaults,Ios,Objective C,Encoding,Nsuserdefaults,我以为我知道是什么导致了这个错误,但我似乎无法找出我做错了什么 以下是我收到的完整错误消息: Attempt to set a non-property-list object ( "<BC_Person: 0x8f3c140>" ) as an NSUserDefaults value for key personDataArray 在应用程序中的某个点上,设置了BC_PersonClass中的NSString,我有一个DataSave类,我认为该类正在处理对我的BC_Per
BC_PersonClass
中的NSString
,我有一个DataSave
类,我认为该类正在处理对我的BC_PersonClass
中的属性进行编码。
下面是我在DataSave
类中使用的代码:
- (void)savePersonArrayData:(BC_Person *)personObject
{
// NSLog(@"name of the person %@", personObject.personsName);
[mutableDataArray addObject:personObject];
// set the temp array to the mutableData array
tempMuteArray = [NSMutableArray arrayWithArray:mutableDataArray];
// save the person object as nsData
NSData *personEncodedObject = [NSKeyedArchiver archivedDataWithRootObject:personObject];
// first add the person object to the mutable array
[tempMuteArray addObject:personEncodedObject];
// NSLog(@"Objects in the array %lu", (unsigned long)mutableDataArray.count);
// now we set that data array to the mutable array for saving
dataArray = [[NSArray alloc] initWithArray:mutableDataArray];
//dataArray = [NSArray arrayWithArray:mutableDataArray];
// save the object to NS User Defaults
NSUserDefaults *userData = [NSUserDefaults standardUserDefaults];
[userData setObject:dataArray forKey:@"personDataArray"];
[userData synchronize];
}
我希望这是足够的代码给你一个想法,我想做什么。
再一次,我知道我的问题在于如何在我的BC_Person类中编码我的属性,我似乎不知道我做错了什么
谢谢你的帮助 您发布的代码试图将自定义对象数组保存到
NSUserDefaults
。你不能那样做。实现NSCoding
方法没有帮助。您只能在nsserdefaults
中存储NSArray
、NSDictionary
、NSData
、NSNumber
和NSDate
等内容
您需要将对象转换为NSData
(就像您在一些代码中所做的那样),并将该NSData
存储在NSUserDefaults
中。如果需要,您甚至可以存储NSData
的NSArray
当您读回数组时,需要取消归档NSData
,以取回BC\u Person
对象
也许你想要这个:
- (void)savePersonArrayData:(BC_Person *)personObject {
[mutableDataArray addObject:personObject];
NSMutableArray *archiveArray = [NSMutableArray arrayWithCapacity:mutableDataArray.count];
for (BC_Person *personObject in mutableDataArray) {
NSData *personEncodedObject = [NSKeyedArchiver archivedDataWithRootObject:personObject];
[archiveArray addObject:personEncodedObject];
}
NSUserDefaults *userData = [NSUserDefaults standardUserDefaults];
[userData setObject:archiveArray forKey:@"personDataArray"];
}
在我看来,自己运行数组并将对象编码到NSData中是相当浪费的。您的错误
BC\u Person是一个非属性列表对象
告诉您框架不知道如何序列化Person对象
因此,需要做的只是确保person对象符合NSCoding,然后您可以简单地将自定义对象数组转换为NSData并将其存储为默认值。这里有一个游乐场:
编辑:写入NSUserDefaults
在Xcode 7上被破坏,因此游乐场将存档到数据并返回并打印输出。UserDefaults步骤包括在内,以防其在以后的某一点被修复
//: Playground - noun: a place where people can play
import Foundation
class Person: NSObject, NSCoding {
let surname: String
let firstname: String
required init(firstname:String, surname:String) {
self.firstname = firstname
self.surname = surname
super.init()
}
//MARK: - NSCoding -
required init(coder aDecoder: NSCoder) {
surname = aDecoder.decodeObjectForKey("surname") as! String
firstname = aDecoder.decodeObjectForKey("firstname") as! String
}
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeObject(firstname, forKey: "firstname")
aCoder.encodeObject(surname, forKey: "surname")
}
}
//: ### Now lets define a function to convert our array to NSData
func archivePeople(people:[Person]) -> NSData {
let archivedObject = NSKeyedArchiver.archivedDataWithRootObject(people as NSArray)
return archivedObject
}
//: ### Create some people
let people = [Person(firstname: "johnny", surname:"appleseed"),Person(firstname: "peter", surname: "mill")]
//: ### Archive our people to NSData
let peopleData = archivePeople(people)
if let unarchivedPeople = NSKeyedUnarchiver.unarchiveObjectWithData(peopleData) as? [Person] {
for person in unarchivedPeople {
print("\(person.firstname), you have been unarchived")
}
} else {
print("Failed to unarchive people")
}
//: ### Lets try use NSUserDefaults
let UserDefaultsPeopleKey = "peoplekey"
func savePeople(people:[Person]) {
let archivedObject = archivePeople(people)
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setObject(archivedObject, forKey: UserDefaultsPeopleKey)
defaults.synchronize()
}
func retrievePeople() -> [Person]? {
if let unarchivedObject = NSUserDefaults.standardUserDefaults().objectForKey(UserDefaultsPeopleKey) as? NSData {
return NSKeyedUnarchiver.unarchiveObjectWithData(unarchivedObject) as? [Person]
}
return nil
}
if let retrievedPeople = retrievePeople() {
for person in retrievedPeople {
print("\(person.firstname), you have been unarchived")
}
} else {
print("Writing to UserDefaults is still broken in playgrounds")
}
瞧,您已经将一组自定义对象存储到了NSUserDefaults中。首先,rmaddy的答案(上面)是正确的:实现
NSCoding
没有帮助。然而,您需要实现NSCoding
来使用NSKeyedArchiver
以及所有这些,所以这只是又一个步骤。。。通过NSData
转换
示例方法
- (NSUserDefaults *) defaults {
return [NSUserDefaults standardUserDefaults];
}
- (void) persistObj:(id)value forKey:(NSString *)key {
[self.defaults setObject:value forKey:key];
[self.defaults synchronize];
}
- (void) persistObjAsData:(id)encodableObject forKey:(NSString *)key {
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:encodableObject];
[self persistObj:data forKey:key];
}
- (id) objectFromDataWithKey:(NSString*)key {
NSData *data = [self.defaults objectForKey:key];
return [NSKeyedUnarchiver unarchiveObjectWithData:data];
}
因此,您可以将NSCoding
对象包装在NSArray
或NSDictionary
或任何东西中 要保存:
NSUserDefaults *currentDefaults = [NSUserDefaults standardUserDefaults];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:yourObject];
[currentDefaults setObject:data forKey:@"yourKeyName"];
要获得:
NSData *data = [currentDefaults objectForKey:@"yourKeyName"];
yourObjectType * token = [NSKeyedUnarchiver unarchiveObjectWithData:data];
删除
[currentDefaults removeObjectForKey:@"yourKeyName"];
我在尝试将字典保存到
NSUserDefaults
时遇到问题。结果证明它不会保存,因为它包含NSNull
值。所以我只是将字典复制到一个可变字典中,删除了空值,然后保存到NSUserDefaults
NSMutableDictionary* dictionary = [NSMutableDictionary dictionaryWithDictionary:dictionary_trying_to_save];
[dictionary removeObjectForKey:@"NullKey"];
[[NSUserDefaults standardUserDefaults] setObject:dictionary forKey:@"key"];
在这种情况下,我知道哪些键可能是NSNull
值。Swift 3解决方案
简单实用程序类
模范班
如何打电话
默认对象必须是属性列表,即NSData、NSString、NSNumber、NSDate、NSArray或NSDictionary的实例(或集合的实例组合)
如果要存储任何其他类型的对象,通常应将其存档以创建NSData实例。有关详细信息,请参阅《首选项和设置编程指南》
Swift-4 Xcode 9.1
尝试此代码
您不能在NSUserDefault中存储映射器,您只能存储NSData、NSString、NSNumber、NSDate、NSArray或NSDictionary。
let myData = NSKeyedArchiver.archivedData(withRootObject: myJson)
UserDefaults.standard.set(myData, forKey: "userJson")
let recovedUserJsonData = UserDefaults.standard.object(forKey: "userJson")
let recovedUserJson = NSKeyedUnarchiver.unarchiveObject(with: recovedUserJsonData)
Swift 5非常简单的方法
//MARK:- First you need to encoded your arr or what ever object you want to save in UserDefaults
//in my case i want to save Picture (NMutableArray) in the User Defaults in
//in this array some objects are UIImage & Strings
//first i have to encoded the NMutableArray
let encodedData = NSKeyedArchiver.archivedData(withRootObject: yourArrayName)
//MARK:- Array save in UserDefaults
defaults.set(encodedData, forKey: "YourKeyName")
//MARK:- When you want to retreive data from UserDefaults
let decoded = defaults.object(forKey: "YourKeyName") as! Data
yourArrayName = NSKeyedUnarchiver.unarchiveObject(with: decoded) as! NSMutableArray
//MARK: Enjoy this arrry "yourArrayName"
Swift 5:可以使用可编码的协议代替NSKeyedArchiever
Pref结构是围绕UserDefaults标准对象的自定义包装
struct Pref {
static let keyUser = "Pref.User"
static var user: User? {
get {
if let data = UserDefaults.standard.object(forKey: keyUser) as? Data {
do {
return try JSONDecoder().decode(User.self, from: data)
} catch {
print("Error while decoding user data")
}
}
return nil
}
set {
if let newValue = newValue {
do {
let data = try JSONEncoder().encode(newValue)
UserDefaults.standard.set(data, forKey: keyUser)
} catch {
print("Error while encoding user data")
}
} else {
UserDefaults.standard.removeObject(forKey: keyUser)
}
}
}
}
因此,您可以这样使用它:
Pref.user?.name = "John"
if let user = Pref.user {...
Swift与
@propertyWrapper
将Codable
对象保存到UserDefault
@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
}
如何使用它
//Set value
GlobalSettings.user = User(name: "Ahmed", pass: "Ahmed")
//GetValue
print(GlobalSettings.user)
我遇到了这个问题,最终发现这是因为我试图使用
NSNumber
作为字典键,而属性列表只允许字符串作为键。的文档没有提到此限制,但其链接的页面有:
按惯例,表2-1中列出的每个可可和核心基础对象被称为属性列表对象。从概念上讲,您可以将“属性列表”视为所有这些类的抽象超类。如果您从某个方法或函数接收属性列表对象,您知道它必须是这些类型之一的实例,但您可能事先不知道是哪种类型。如果属性列表对象是容器(即数组或字典),则其中包含的所有对象也必须是属性列表对象。如果数组或字典包含的对象不是属性列表对象,则无法使用各种属性列表方法和函数保存和恢复数据层次结构。尽管NSDictionary和CFDictionary对象允许其键为任何类型的对象,但如果键不是字符串对象,则集合不是属性列表对象
(强调我的)我有一个问题。如果我想将personEncodedObject添加到数组中,然后将数组放入用户数据中。。。我是否可以替换:[archiveArray addObject:personEncodedObject];使用NSArray并将addObject:personEncodedObject添加到该数组中,然后将其保存在userData中?如果你听我的话,嗯?我认为你输入错了,因为你想用同一行代码替换一行代码。我的代码确实将编码对象数组放入了用户默认值中。我想我迷路了,因为我以为你只能使用
struct User: Codable {
let id: String
let mail: String
let fullName: String
}
struct Pref {
static let keyUser = "Pref.User"
static var user: User? {
get {
if let data = UserDefaults.standard.object(forKey: keyUser) as? Data {
do {
return try JSONDecoder().decode(User.self, from: data)
} catch {
print("Error while decoding user data")
}
}
return nil
}
set {
if let newValue = newValue {
do {
let data = try JSONEncoder().encode(newValue)
UserDefaults.standard.set(data, forKey: keyUser)
} catch {
print("Error while encoding user data")
}
} else {
UserDefaults.standard.removeObject(forKey: keyUser)
}
}
}
}
Pref.user?.name = "John"
if let user = Pref.user {...
@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)