ios 尝试将非属性列表对象设置为 NSUserDefaults
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19720611/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Attempt to set a non-property-list object as an NSUserDefaults
提问by icekomo
I thought I knew what was causing this error, but I can't seem to figure out what I did wrong.
我以为我知道是什么导致了这个错误,但我似乎无法弄清楚我做错了什么。
Here is the full error message I am getting:
这是我收到的完整错误消息:
Attempt to set a non-property-list object ( "<BC_Person: 0x8f3c140>" ) as an NSUserDefaults value for key personDataArray
I have a Person
class that I think is conforming to the NSCoding
protocol, where I have both of these methods in my person class:
我有一个Person
我认为符合NSCoding
协议的类,我的个人类中有这两种方法:
- (void)encodeWithCoder:(NSCoder *)coder {
[coder encodeObject:self.personsName forKey:@"BCPersonsName"];
[coder encodeObject:self.personsBills forKey:@"BCPersonsBillsArray"];
}
- (id)initWithCoder:(NSCoder *)coder {
self = [super init];
if (self) {
self.personsName = [coder decodeObjectForKey:@"BCPersonsName"];
self.personsBills = [coder decodeObjectForKey:@"BCPersonsBillsArray"];
}
return self;
}
At some point in the app, the NSString
in the BC_PersonClass
is set, and I have a DataSave
class that I think is handling the encoding the properties in my BC_PersonClass
.
Here is the code I am using from the DataSave
class:
在应用程序的某个时刻,设置了NSString
in BC_PersonClass
,并且我有一个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];
}
I hope this is enough code to give you an idea o what I am trying to do. Again I know my problem lie with how I am encoding my properties in my BC_Person class, I just can't seem to figure out what though I'm doing wrong.
我希望这是足够的代码,让您了解我正在尝试做什么。我再次知道我的问题在于我如何在我的 BC_Person 类中编码我的属性,我似乎无法弄清楚我做错了什么。
Thanks for the help!
谢谢您的帮助!
回答by rmaddy
The code you posted tries to save an array of custom objects to NSUserDefaults
. You can't do that. Implementing the NSCoding
methods doesn't help. You can only store things like NSArray
, NSDictionary
, NSString
, NSData
, NSNumber
, and NSDate
in NSUserDefaults
.
您发布的代码尝试将一组自定义对象保存到NSUserDefaults
. 你不能那样做。实施这些NSCoding
方法无济于事。只能存储之类的东西NSArray
,NSDictionary
,NSString
,NSData
,NSNumber
,和NSDate
在NSUserDefaults
。
You need to convert the object to NSData
(like you have in some of the code) and store that NSData
in NSUserDefaults
. You can even store an NSArray
of NSData
if you need to.
您需要将对象转换为NSData
(就像您在某些代码中所做的NSData
那样)并将其存储在NSUserDefaults
. 你甚至可以存储NSArray
的NSData
,如果你需要。
When you read back the array you need to unarchive the NSData
to get back your BC_Person
objects.
当您读回数组时,您需要取消归档NSData
以取回您的BC_Person
对象。
Perhaps you want this:
也许你想要这个:
- (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"];
}
回答by Daniel Galasko
It seems rather wasteful to me to run through the array and encode the objects into NSData yourself. Your error BC_Person is a non-property-list object
is telling you that the framework doesn't know how to serialize your person object.
对我来说,遍历数组并将对象自己编码为 NSData 似乎相当浪费。您的错误BC_Person is a non-property-list object
告诉您该框架不知道如何序列化您的 person 对象。
So all that is needed is to ensure that your person object conforms to NSCoding then you can simply convert your array of custom objects into NSData and store that to defaults. Heres a playground:
因此,所需要做的就是确保您的 person 对象符合 NSCoding,然后您可以简单地将自定义对象数组转换为 NSData 并将其存储为默认值。这是一个游乐场:
Edit: Writing to NSUserDefaults
is broken on Xcode 7 so the playground will archive to data and back and print an output. The UserDefaults step is included in case its fixed at a later point
编辑: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")
}
And Voila, you have stored an array of custom objects into NSUserDefaults
瞧,您已经将一组自定义对象存储到 NSUserDefaults 中
回答by jose920405
To save:
保存:
NSUserDefaults *currentDefaults = [NSUserDefaults standardUserDefaults];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:yourObject];
[currentDefaults setObject:data forKey:@"yourKeyName"];
To Get:
要得到:
NSData *data = [currentDefaults objectForKey:@"yourKeyName"];
yourObjectType * token = [NSKeyedUnarchiver unarchiveObjectWithData:data];
For Remove
删除
[currentDefaults removeObjectForKey:@"yourKeyName"];
回答by Sazzad Hissain Khan
Swift 3 Solution
Swift 3 解决方案
Simple utility class
简单实用类
class ArchiveUtil {
private static let PeopleKey = "PeopleKey"
private static func archivePeople(people : [Human]) -> NSData {
return NSKeyedArchiver.archivedData(withRootObject: people as NSArray) as NSData
}
static func loadPeople() -> [Human]? {
if let unarchivedObject = UserDefaults.standard.object(forKey: PeopleKey) as? Data {
return NSKeyedUnarchiver.unarchiveObject(with: unarchivedObject as Data) as? [Human]
}
return nil
}
static func savePeople(people : [Human]?) {
let archivedObject = archivePeople(people: people!)
UserDefaults.standard.set(archivedObject, forKey: PeopleKey)
UserDefaults.standard.synchronize()
}
}
Model Class
模型类
class Human: NSObject, NSCoding {
var name:String?
var age:Int?
required init(n:String, a:Int) {
name = n
age = a
}
required init(coder aDecoder: NSCoder) {
name = aDecoder.decodeObject(forKey: "name") as? String
age = aDecoder.decodeInteger(forKey: "age")
}
public func encode(with aCoder: NSCoder) {
aCoder.encode(name, forKey: "name")
aCoder.encode(age, forKey: "age")
}
}
How to call
如何打电话
var people = [Human]()
people.append(Human(n: "Sazzad", a: 21))
people.append(Human(n: "Hissain", a: 22))
people.append(Human(n: "Khan", a: 23))
ArchiveUtil.savePeople(people: people)
let others = ArchiveUtil.loadPeople()
for human in others! {
print("name = \(human.name!), age = \(human.age!)")
}
回答by Vishal Vaghasiya
Swift- 4 Xcode 9.1
Swift-4 Xcode 9.1
try this code
试试这个代码
you can not store mapper in NSUserDefault, you can only store NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary.
你不能在 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)
回答by Dan Rosenstark
First off, rmaddy's answer (above) is right: implementing NSCoding
doesn't help. However, you need to implement NSCoding
to use NSKeyedArchiver
and all that, so it's just one more step... converting via NSData
.
首先,rmaddy 的回答(以上)是正确的:实施NSCoding
无济于事。但是,您需要实现NSCoding
以使用NSKeyedArchiver
和所有这些,所以它只是一个步骤...转换通过NSData
.
Example methods
示例方法
- (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];
}
So you can wrap your NSCoding
objects in an NSArray
or NSDictionary
or whatever...
因此,您可以将NSCoding
对象包装在NSArray
或NSDictionary
或其他任何...
回答by simple_code
I had this problem trying save a dictionary to NSUserDefaults
. It turns out it wouldn't save because it contained NSNull
values. So I just copied the dictionary into a mutable dictionary removed the nulls then saved to NSUserDefaults
我在尝试将字典保存到NSUserDefaults
. 事实证明它不会保存,因为它包含NSNull
值。所以我只是将字典复制到可变字典中删除了空值然后保存到NSUserDefaults
NSMutableDictionary* dictionary = [NSMutableDictionary dictionaryWithDictionary:dictionary_trying_to_save];
[dictionary removeObjectForKey:@"NullKey"];
[[NSUserDefaults standardUserDefaults] setObject:dictionary forKey:@"key"];
In this case I knew which keys might be NSNull
values.
在这种情况下,我知道哪些键可能是NSNull
值。
回答by Shakeel Ahmed
Swift 5Very Easy way
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"
回答by Prcela
Swift 5: The Codableprotocol can be used instead of NSKeyedArchiever.
Swift 5:可以使用Codable协议代替NSKeyedArchiever。
struct User: Codable {
let id: String
let mail: String
let fullName: String
}
The Prefstruct is custom wrapper around the UserDefaults standard object.
在县结构是围绕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)
}
}
}
}
So you can use it this way:
所以你可以这样使用它:
Pref.user?.name = "John"
if let user = Pref.user {...
回答by dimo hamdy
Swiftwith @propertyWrapper
斯威夫特与@propertyWrapper
Save Codable
object to UserDefault
将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
}
Example User model confirm Codable
示例用户模型确认可编码
struct User:Codable {
let name:String
let pass:String
}
How to use it
如何使用它
//Set value
GlobalSettings.user = User(name: "Ahmed", pass: "Ahmed")
//GetValue
print(GlobalSettings.user)