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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 02:42:28  来源:igfitidea点击:

Attempt to set a non-property-list object as an NSUserDefaults

iosobjective-cencodingnsuserdefaults

提问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 Personclass that I think is conforming to the NSCodingprotocol, 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 NSStringin the BC_PersonClassis set, and I have a DataSaveclass that I think is handling the encoding the properties in my BC_PersonClass. Here is the code I am using from the DataSaveclass:

在应用程序的某个时刻,设置了NSStringin 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 NSCodingmethods doesn't help. You can only store things like NSArray, NSDictionary, NSString, NSData, NSNumber, and NSDatein NSUserDefaults.

您发布的代码尝试将一组自定义对象保存到NSUserDefaults. 你不能那样做。实施这些NSCoding方法无济于事。只能存储之类的东西NSArrayNSDictionaryNSStringNSDataNSNumber,和NSDateNSUserDefaults

You need to convert the object to NSData(like you have in some of the code) and store that NSDatain NSUserDefaults. You can even store an NSArrayof NSDataif you need to.

您需要将对象转换为NSData(就像您在某些代码中所做的NSData那样)并将其存储在NSUserDefaults. 你甚至可以存储NSArrayNSData,如果你需要。

When you read back the array you need to unarchive the NSDatato get back your BC_Personobjects.

当您读回数组时,您需要取消归档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 objectis 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 NSUserDefaultsis 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 NSCodingdoesn't help. However, you need to implement NSCodingto use NSKeyedArchiverand 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 NSCodingobjects in an NSArrayor NSDictionaryor whatever...

因此,您可以将NSCoding对象包装在NSArrayNSDictionary或其他任何...

回答by simple_code

I had this problem trying save a dictionary to NSUserDefaults. It turns out it wouldn't save because it contained NSNullvalues. 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 NSNullvalues.

在这种情况下,我知道哪些键可能是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 Codableobject 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)