web-dev-qa-db-fra.com

Tentative de définir un objet non-liste de propriétés en tant que NSUserDefaults

Je pensais savoir ce qui causait cette erreur, mais je n'arrive pas à comprendre ce que j'ai mal fait.

Voici le message d'erreur complet que je reçois:

 Essayez de définir un objet non-liste de propriétés (
 "<BC_Person: 0x8f3c140>" 
) En tant que valeur NSUserDefaults pour la clé personDataArray 
.

J'ai une classe Person qui, à mon avis, est conforme au protocole NSCoding, où j'ai les deux méthodes suivantes dans ma classe 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;
}

À un moment donné dans l'application, la NSString du BC_PersonClass est définie et j'ai une classe DataSave qui gère le codage des propriétés dans mon BC_PersonClass. Voici le code que j'utilise dans la 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];
}

J'espère que c'est assez de code pour vous donner une idée de ce que j'essaie de faire. Encore une fois, je sais que mon problème réside dans la façon dont je code mes propriétés dans ma classe BC_Person. Je n'arrive tout simplement pas à comprendre ce que je fais mal.

Merci pour l'aide!

181
icekomo

Le code que vous avez posté tente de sauvegarder un tableau d'objets personnalisés dans NSUserDefaults. Tu ne peux pas faire ça. Implémenter les méthodes NSCoding n'aide pas. Vous pouvez uniquement stocker des éléments tels que NSArray, NSDictionary, NSString, NSData, NSNumber et NSDate dans NSUserDefaults.

Vous devez convertir l'objet en NSData (comme dans certains codes) et stocker ce NSData dans NSUserDefaults. Vous pouvez même stocker une NSArray sur NSData si vous en avez besoin.

Lorsque vous relisez le tableau, vous devez désarchiver le NSData pour récupérer vos objets BC_Person.

Peut-être que vous voulez ceci:

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

Il me semble plutôt inutile de parcourir le tableau et d’encoder vous-même les objets dans NSData. Votre erreur BC_Person is a non-property-list object vous indique que le framework ne sait pas sérialiser votre objet personne.

Il suffit donc de vous assurer que votre objet personnel est conforme à NSCoding. Vous pouvez simplement convertir votre tableau d'objets personnalisés en NSData et le stocker comme valeur par défaut. Heres un terrain de jeu:

Edit: L'écriture sur NSUserDefaults est interrompue sur Xcode 7, de sorte que le terrain de jeu archivera les données, en remontera et imprimera une sortie. L'étape UserDefaults est incluse au cas où elle serait fixée ultérieurement.

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

Et voila, vous avez stocké un tableau d'objets personnalisés dans NSUserDefaults

66
Daniel Galasko

Sauver:

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

Obtenir:

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

Pour supprimer

[currentDefaults removeObjectForKey:@"yourKeyName"];
48
jose920405

Swift 3 Solution

Classe d'utilité 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()
    }

}

Classe de modèle

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

    }
}

Comment appeler

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!)")
}
31
Sazzad Hissain Khan

Swift- 4 Xcode 9.1

essayez ce code

vous ne pouvez pas stocker le mappeur dans NSUserDefault, vous ne pouvez stocker que NSData, NSString, NSNumber, NSDate, NSArray ou 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)
12
Vishal Vaghasiya

Tout d'abord, la réponse de rmaddy (ci-dessus) est juste: implémenter NSCoding n'aide pas. Cependant, vous devez implémenter NSCoding pour utiliser NSKeyedArchiver et tout le reste. Il ne reste donc qu'une étape de plus ... la conversion via NSData.

Exemple de méthodes

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

Ainsi, vous pouvez envelopper vos objets NSCoding dans un NSArray ou NSDictionary ou autre chose ...

10
Dan Rosenstark

J'ai eu ce problème en essayant de sauvegarder un dictionnaire sur NSUserDefaults. Il s'avère que l'enregistrement n'a pas été effectué car il contenait les valeurs NSNull. Donc, je viens de copier le dictionnaire dans un dictionnaire mutable supprimé les NULL, puis enregistré dans NSUserDefaults

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

Dans ce cas, je savais quelles clés pourraient être NSNull valeurs.

5
simple_code

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

Un objet par défaut doit être une liste de propriétés, c'est-à-dire une instance de (ou pour des collections, une combinaison d'instances de): NSData, NSString, NSNumber, NSDate, NSArray ou NSDictionary.

Si vous souhaitez stocker un autre type d'objet, vous devez généralement l'archiver pour créer une instance de NSData. Pour plus de détails, voir Guide de programmation des préférences et paramètres.

Swift 5: Le protocole Codable peut être utilisé à la place de NSKeyedArchiever.

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

La structure Pref est un wrapper personnalisé autour de l'objet standard 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)
            }
        }
    }
}

Vous pouvez donc l'utiliser de cette façon:

Pref.user?.name = "John"

if let user = Pref.user {...
1
Prcela

Swift 5 Très facile

//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"
0
Shakeel Ahmed