ios - Tentativo di impostare un oggetto non-elenco di proprietà come un NSUserDefaults




objective-c encoding (5)

Pensavo di sapere cosa stava causando questo errore, ma non riesco a capire cosa ho sbagliato.

Ecco il messaggio di errore completo che sto ottenendo:

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

Ho una classe Person che ritengo sia conforme al protocollo NSCoding , in cui ho entrambi questi metodi nella mia classe:

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

Ad un certo punto nell'app, la NSString in BC_PersonClass è impostata, e ho una classe DataSave che penso stia gestendo la codifica delle proprietà nel mio BC_PersonClass . Ecco il codice che sto usando dalla classe 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];
}

Spero che questo codice sia sufficiente per darti un'idea di ciò che sto cercando di fare. Ancora una volta so che il mio problema sta nel modo in cui sto codificando le mie proprietà nella mia classe BC_Person, ma non riesco a capire cosa sto facendo.

Grazie per l'aiuto!


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

Un oggetto predefinito deve essere un elenco di proprietà, ovvero un'istanza di (o per le raccolte, una combinazione di istanze di): NSData, NSString, NSNumber, NSDate, NSArray o NSDictionary.

Se si desidera archiviare qualsiasi altro tipo di oggetto, in genere è necessario archiviarlo per creare un'istanza di NSData. Per ulteriori dettagli, vedere Preferenze e impostazioni Guida alla programmazione.


Soluzione Swift 3

Classe di utilità semplice

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

}

Classe di modello

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

    }
}

Come chiamare

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

Il codice che hai postato tenta di salvare una serie di oggetti personalizzati su NSUserDefaults . Non puoi farlo. L'implementazione dei metodi NSCoding non aiuta. È possibile memorizzare solo elementi come NSArray , NSDictionary , NSString , NSData , NSNumber e NSDate in NSUserDefaults .

È necessario convertire l'oggetto in NSData (come nel codice) e memorizzare NSData in NSUserDefaults . È anche possibile memorizzare un NSArray di NSData se necessario.

Quando NSData l'array devi annullare l'archiviazione di NSData per recuperare gli oggetti BC_Person .

Forse vuoi questo:

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

Mi sembra piuttosto dispendioso eseguire l'array e codificare gli oggetti direttamente in NSData. Il tuo errore BC_Person is a non-property-list object che ti dice che il framework non sa come serializzare il tuo oggetto person.

Quindi tutto ciò che è necessario è assicurarsi che il proprio oggetto sia conforme a NSCoding, quindi è sufficiente convertire la serie di oggetti personalizzati in NSData e archiviarla ai valori predefiniti. Ecco un parco giochi:

Modifica : la scrittura su NSUserDefaults è interrotta su Xcode 7, quindi il parco giochi verrà archiviato in dati e tornerà e verrà stampato un output. Il passaggio UserDefaults è incluso nel caso in cui sia stato risolto in un secondo momento

//: 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")
}

E Voila, hai archiviato una serie di oggetti personalizzati in NSUserDefaults


Salvare:

NSUserDefaults *currentDefaults = [NSUserDefaults standardUserDefaults];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:yourObject];
[currentDefaults setObject:data forKey:@"yourKeyName"];

Ottenere:

NSData *data = [currentDefaults objectForKey:@"yourKeyName"];
yourObjectType * token = [NSKeyedUnarchiver unarchiveObjectWithData:data];

Per rimuovere

[currentDefaults removeObjectForKey:@"yourKeyName"];




nsuserdefaults