ios - non - Intentar establecer un objeto que no sea de propiedad como NSUserDefaults
attempt to insert non-property list object swift 4 (7)
Pensé que sabía lo que estaba causando este error, pero parece que no puedo entender lo que hice mal.
Aquí está el mensaje de error completo que recibo:
Attempt to set a non-property-list object ( "<BC_Person: 0x8f3c140>" ) as an NSUserDefaults value for key personDataArray
Tengo una clase Person
que creo que se ajusta al protocolo NSCoding
, donde tengo ambos métodos en mi clase de persona:
- (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;
}
En algún momento de la aplicación, se establece NSString
en BC_PersonClass
, y tengo una clase DataSave
que creo que maneja la codificación de las propiedades en BC_PersonClass
. Aquí está el código que estoy usando de la clase 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];
}
Espero que esto sea suficiente para darte una idea de lo que estoy tratando de hacer. Una vez más, sé que mi problema radica en cómo estoy codificando mis propiedades en mi clase BC_Person, simplemente no puedo entender qué es lo que estoy haciendo mal.
¡Gracias por la ayuda!
https://developer.apple.com/reference/foundation/userdefaults
Un objeto predeterminado debe ser una lista de propiedades, es decir, una instancia de (o para colecciones, una combinación de instancias de): NSData, NSString, NSNumber, NSDate, NSArray o NSDictionary.
Si desea almacenar cualquier otro tipo de objeto, normalmente debe archivarlo para crear una instancia de NSData. Para obtener más detalles, consulte la Guía de programación de preferencias y configuraciones.
Solución Swift 3
Clase de utilidad simple
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()
}
}
Clase de modelo
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")
}
}
Como llamar
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!)")
}
Ahorrar:
NSUserDefaults *currentDefaults = [NSUserDefaults standardUserDefaults];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:yourObject];
[currentDefaults setObject:data forKey:@"yourKeyName"];
Llegar:
NSData *data = [currentDefaults objectForKey:@"yourKeyName"];
yourObjectType * token = [NSKeyedUnarchiver unarchiveObjectWithData:data];
Para quitar
[currentDefaults removeObjectForKey:@"yourKeyName"];
El código que ha publicado intenta guardar una matriz de objetos personalizados en NSUserDefaults
. No puedes hacer eso. Implementar los métodos de NSCoding
no ayuda. Solo puede almacenar elementos como NSArray
, NSDictionary
, NSString
, NSData
, NSNumber
y NSDate
en NSUserDefaults
.
Necesita convertir el objeto a NSData
(como lo ha hecho en algunos de los códigos) y almacenar ese NSData
en NSUserDefaults
. Incluso puede almacenar un NSArray
de NSData
si lo necesita.
Cuando vuelve a leer la matriz, necesita desarchivar NSData
para recuperar sus objetos BC_Person
.
Quizás quieras esto:
- (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"];
}
En primer lugar, la respuesta de rmaddy (arriba) es correcta: la implementación de NSCoding
no ayuda. Sin embargo, debe implementar NSCoding
para usar NSKeyedArchiver
y todo eso, por lo que es un paso más ... la conversión a través de NSData
.
Métodos de ejemplo
- (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];
}
Para que pueda envolver sus objetos NSCoding
en un NSArray
o NSDictionary
o lo que sea ...
Me parece un desperdicio correr por la matriz y codificar los objetos en NSData usted mismo. Su error BC_Person is a non-property-list object
le dice que el marco no sabe cómo serializar su objeto personal.
Entonces, todo lo que se necesita es garantizar que su objeto personal se ajuste a NSCoding, luego simplemente puede convertir su conjunto de objetos personalizados en NSData y almacenarlos en sus valores predeterminados. Aquí hay un patio de recreo:
Editar : Escribir en NSUserDefaults
se interrumpe en Xcode 7, por lo que el área de juegos se archivará en los datos y volverá e imprimirá una salida. El paso UserDefaults está incluido en caso de que se solucione en un punto posterior
//: 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")
}
Y Voila, has almacenado una matriz de objetos personalizados en NSUserDefaults
Tuve este problema intentando guardar un diccionario en NSUserDefaults
. Resulta que no se guardaría porque contenía valores NSNull
. Así que simplemente copié el diccionario en un diccionario mutable, NSUserDefaults
nulos y luego los guarde en NSUserDefaults
NSMutableDictionary* dictionary = [NSMutableDictionary dictionaryWithDictionary:dictionary_trying_to_save];
[dictionary removeObjectForKey:@"NullKey"];
[[NSUserDefaults standardUserDefaults] setObject:dictionary forKey:@"key"];
En este caso, sabía qué claves podrían ser valores NSNull
.