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!
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"];
}
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
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"];
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!)")
}
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)
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 ...
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.
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 {...
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"