Попытка установить объект не-property-list как NSUserDefaults

Я думал, что знаю, что вызывает эту ошибку, но я не могу понять, что я сделал неправильно.

Вот полное сообщение об ошибке, которое я получаю:

Attempt to set a non-property-list object (
   "<BC_Person: 0x8f3c140>"
) as an NSUserDefaults value for key personDataArray

У меня есть класс Person, который, как мне кажется, соответствует протоколу NSCoding, где у меня есть оба этих метода в моем классе person:

- (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;
}

В какой-то момент приложения NSString в 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];
}

Надеюсь, этого достаточно кода, чтобы дать вам представление о том, что я пытаюсь сделать. Снова я знаю, что моя проблема заключается в том, как я кодирую свои свойства в классе BC_Person, я просто не могу понять, что, хотя я делаю неправильно.

Спасибо за помощь!

Ответ 1

Код, который вы отправили, пытается сохранить массив настраиваемых объектов до NSUserDefaults. Вы не можете этого сделать. Внедрение методов NSCoding не помогает. Вы можете хранить такие вещи, как NSArray, NSDictionary, NSString, NSData, NSNumber и NSDate в NSUserDefaults.

Вам нужно преобразовать объект в NSData (например, у вас есть в некотором коде) и сохранить его NSData в NSUserDefaults. Вы даже можете сохранить NSArray NSData, если вам нужно.

Когда вы читаете массив, вам нужно разблокировать NSData, чтобы вернуть ваши объекты BC_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"];
}

Ответ 2

Мне кажется довольно расточительным пробегать массив и кодировать объекты в NSData самостоятельно. Ваша ошибка BC_Person is a non-property-list object сообщает вам, что структура не знает, как сериализовать ваш объект 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")
}

И Voila, вы сохранили массив пользовательских объектов в NSUserDefaults

Ответ 3

Чтобы сохранить:

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"];

Ответ 4

Решение Swift 3

Простой класс утилиты

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()
    }

}

Класс модели

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")

    }
}

Как позвонить

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!)")
}

Ответ 5

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)

Ответ 6

Во-первых, ответ 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 или что-то еще...

Ответ 7

У меня возникла проблема с поиском словаря на NSUserDefaults. Оказывается, это не спасет, поскольку в нем содержатся значения NSNull. Поэтому я просто скопировал словарь в изменяемый словарь, удалил нули, а затем сохранил его в NSUserDefaults

NSMutableDictionary* dictionary = [NSMutableDictionary dictionaryWithDictionary:dictionary_trying_to_save];
[dictionary removeObjectForKey:@"NullKey"];
[[NSUserDefaults standardUserDefaults] setObject:dictionary forKey:@"key"];

В этом случае я знал, какие ключи могут быть NSNull.

Ответ 8

https://developer.apple.com/reference/foundation/userdefaults

Объектом по умолчанию должен быть список свойств, то есть экземпляр (или для коллекций, комбинация экземпляров): NSData, NSString, NSNumber, NSDate, NSArray или NSDictionary.

Если вы хотите сохранить какой-либо объект другого типа, вы должны архивировать его, чтобы создать экземпляр NSData. Для получения дополнительной информации см. Руководство по программированию и настройкам.

Ответ 9

Свифт 5: Протокол Кодируемый может быть использован вместо NSKeyedArchiever.

struct User: Codable {
    let id: String
    let mail: String
    let fullName: String
}

Структура 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 {...

Ответ 10

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"