users standard iphone objective-c encoding nsuserdefaults

iphone - standard - userdefaults swift 4



Cómo almacenar objetos personalizados en NSUserDefaults (7)

Muy bien, así que he estado hurgando un poco, y me doy cuenta de mi problema, pero no sé cómo solucionarlo. He hecho una clase personalizada para guardar algunos datos. Hago objetos para esta clase, y los necesito para durar entre sesiones. Antes de poner toda mi información en NSUserDefaults , pero esto no está funcionando.

-[NSUserDefaults setObject:forKey:]: Attempt to insert non-property value ''<Player: 0x3b0cc90>'' of class ''Player''.

Ese es el mensaje de error que recibo cuando coloco mi clase personalizada, "Player", en NSUserDefaults . Ahora, he leído que aparentemente NSUserDefaults solo almacena algunos tipos de información. Entonces, ¿cómo puedo obtener mis objetos en NSUSerDefaults ?

Leí que debería haber una forma de "codificar" mi objeto personalizado y luego colocarlo, pero no estoy seguro de cómo implementarlo, ¡se agradecería la ayuda! ¡Gracias!

****EDITAR****

Muy bien, entonces trabajé con el código que figura a continuación (¡Gracias!), Pero todavía tengo algunos problemas. Básicamente, el código falla ahora y no estoy seguro de por qué, porque no da ningún error. Tal vez me esté perdiendo algo básico y estoy demasiado cansado, pero ya veremos. Aquí está la implementación de mi clase personalizada, "Jugador":

@interface Player : NSObject { NSString *name; NSNumber *life; //Log of player''s life } //Getting functions, return the info - (NSString *)name; - (int)life; - (id)init; //These are the setters - (void)setName:(NSString *)input; //string - (void)setLife:(NSNumber *)input; //number @end

Archivo de implementación:

#import "Player.h" @implementation Player - (id)init { if (self = [super init]) { [self setName:@"Player Name"]; [self setLife:[NSNumber numberWithInt:20]]; [self setPsnCounters:[NSNumber numberWithInt:0]]; } return self; } - (NSString *)name {return name;} - (int)life {return [life intValue];} - (void)setName:(NSString *)input { [input retain]; if (name != nil) { [name release]; } name = input; } - (void)setLife:(NSNumber *)input { [input retain]; if (life != nil) { [life release]; } life = input; } /* This code has been added to support encoding and decoding my objecst */ -(void)encodeWithCoder:(NSCoder *)encoder { //Encode the properties of the object [encoder encodeObject:self.name forKey:@"name"]; [encoder encodeObject:self.life forKey:@"life"]; } -(id)initWithCoder:(NSCoder *)decoder { self = [super init]; if ( self != nil ) { //decode the properties self.name = [decoder decodeObjectForKey:@"name"]; self.life = [decoder decodeObjectForKey:@"life"]; } return self; } -(void)dealloc { [name release]; [life release]; [super dealloc]; } @end

Así que esa es mi clase, bastante sencilla, sé que funciona para hacer mis objetos. Así que aquí están las partes relevantes del archivo AppDelegate (donde llamo a las funciones de cifrado y descifrado):

@class MainViewController; @interface MagicApp201AppDelegate : NSObject <UIApplicationDelegate> { UIWindow *window; MainViewController *mainViewController; } @property (nonatomic, retain) IBOutlet UIWindow *window; @property (nonatomic, retain) MainViewController *mainViewController; -(void)saveCustomObject:(Player *)obj; -(Player *)loadCustomObjectWithKey:(NSString*)key; @end

Y luego las partes importantes del archivo de implementación:

#import "MagicApp201AppDelegate.h" #import "MainViewController.h" #import "Player.h" @implementation MagicApp201AppDelegate @synthesize window; @synthesize mainViewController; - (void)applicationDidFinishLaunching:(UIApplication *)application { NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults]; //First check to see if some things exist int startup = [prefs integerForKey:@"appHasLaunched"]; if (startup == nil) { //Make the single player Player *singlePlayer = [[Player alloc] init]; NSLog([[NSString alloc] initWithFormat:@"%@/n%d/n%d",[singlePlayer name], [singlePlayer life], [singlePlayer psnCounters]]); // test //Encode the single player so it can be stored in UserDefaults id test = [MagicApp201AppDelegate new]; [test saveCustomObject:singlePlayer]; [test release]; } [prefs synchronize]; } -(void)saveCustomObject:(Player *)object { NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults]; NSData *myEncodedObject = [NSKeyedArchiver archivedDataWithRootObject:object]; [prefs setObject:myEncodedObject forKey:@"testing"]; } -(Player *)loadCustomObjectWithKey:(NSString*)key { NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults]; NSData *myEncodedObject = [prefs objectForKey:key ]; Player *obj = (Player *)[NSKeyedUnarchiver unarchiveObjectWithData: myEncodedObject]; return obj; }

Eeee, lo siento por todo el código. Sólo trato de ayudar. Básicamente, la aplicación se iniciará y luego se bloqueará inmediatamente. Lo he reducido a la parte de cifrado de la aplicación, ahí es donde se bloquea, así que estoy haciendo algo mal, pero no estoy seguro de qué. La ayuda sería apreciada de nuevo, gracias!

(No he logrado descifrar todavía, ya que todavía no he logrado que el cifrado funcione).


Swift 4

Introdujo el protocolo Codable que hace toda la magia para este tipo de tareas. Solo conforma tu estructura / clase personalizada a ella:

struct Player: Codable { let name: String let life: Double }

Y para almacenar en los valores predeterminados, puede utilizar el PropertyListEncoder / Decoder :

let player = Player(name: "Jim", life: 3.14) UserDefaults.standard.set(try! PropertyListEncoder().encode(player), forKey: kPlayerDefaultsKey) let storedObject: Data = UserDefaults.standard.object(forKey: kPlayerDefaultsKey) as! Data let storedPlayer: Player = try! PropertyListDecoder().decode(Player.self, from: storedObject)

Funcionará así para matrices y otras clases de contenedores de tales objetos:

try! PropertyListDecoder().decode([Player].self, from: storedArray)


Creo una biblioteca RMMapper ( https://github.com/roomorama/RMMapper ) para ayudar a guardar objetos personalizados en NSUserDefaults más fácil y más conveniente, porque la implementación de encodeWithCoder y initWithCoder es súper aburrido.

Para marcar una clase como archivable, simplemente use: #import "NSObject+RMArchivable.h"

Para guardar un objeto personalizado en NSUserDefaults:

#import "NSUserDefaults+RMSaveCustomObject.h" NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; [defaults rm_setCustomObject:user forKey:@"SAVED_DATA"];

Para obtener obj personalizado de NSUserDefaults:

user = [defaults rm_customObjectForKey:@"SAVED_DATA"];


En su clase de Jugador, implemente los siguientes dos métodos (sustituyendo las llamadas a encodeObject con algo relevante para su propio objeto):

- (void)encodeWithCoder:(NSCoder *)encoder { //Encode properties, other class variables, etc [encoder encodeObject:self.question forKey:@"question"]; [encoder encodeObject:self.categoryName forKey:@"category"]; [encoder encodeObject:self.subCategoryName forKey:@"subcategory"]; } - (id)initWithCoder:(NSCoder *)decoder { if((self = [super init])) { //decode properties, other class vars self.question = [decoder decodeObjectForKey:@"question"]; self.categoryName = [decoder decodeObjectForKey:@"category"]; self.subCategoryName = [decoder decodeObjectForKey:@"subcategory"]; } return self; }

Lectura y escritura de NSUserDefaults :

- (void)saveCustomObject:(MyObject *)object key:(NSString *)key { NSData *encodedObject = [NSKeyedArchiver archivedDataWithRootObject:object]; NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; [defaults setObject:encodedObject forKey:key]; [defaults synchronize]; } - (MyObject *)loadCustomObjectWithKey:(NSString *)key { NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; NSData *encodedObject = [defaults objectForKey:key]; MyObject *object = [NSKeyedUnarchiver unarchiveObjectWithData:encodedObject]; return object; }

Código prestado descaradamente de: guardando clase en nsuserdefaults


Sincronice los datos / objetos que ha guardado en NSUserDefaults

-(void)saveCustomObject:(Player *)object { NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults]; NSData *myEncodedObject = [NSKeyedArchiver archivedDataWithRootObject:object]; [prefs setObject:myEncodedObject forKey:@"testing"]; [prefs synchronize]; }

Espero que esto te ayudará. Gracias


Tomando la respuesta de @ chrissr y ejecutándola, este código se puede implementar en una categoría agradable en NSUserDefaults para guardar y recuperar objetos personalizados:

@interface NSUserDefaults (NSUserDefaultsExtensions) - (void)saveCustomObject:(id<NSCoding>)object key:(NSString *)key; - (id<NSCoding>)loadCustomObjectWithKey:(NSString *)key; @end @implementation NSUserDefaults (NSUserDefaultsExtensions) - (void)saveCustomObject:(id<NSCoding>)object key:(NSString *)key { NSData *encodedObject = [NSKeyedArchiver archivedDataWithRootObject:object]; [self setObject:encodedObject forKey:key]; [self synchronize]; } - (id<NSCoding>)loadCustomObjectWithKey:(NSString *)key { NSData *encodedObject = [self objectForKey:key]; id<NSCoding> object = [NSKeyedUnarchiver unarchiveObjectWithData:encodedObject]; return object; } @end

Uso:

[[NSUserDefaults standardUserDefaults] saveCustomObject:myObject key:@"myKey"];


Si alguien está buscando una versión rápida:

1) Crea una clase personalizada para tus datos

class customData: NSObject, NSCoding { let name : String let url : String let desc : String init(tuple : (String,String,String)){ self.name = tuple.0 self.url = tuple.1 self.desc = tuple.2 } func getName() -> String { return name } func getURL() -> String{ return url } func getDescription() -> String { return desc } func getTuple() -> (String,String,String) { return (self.name,self.url,self.desc) } required init(coder aDecoder: NSCoder) { self.name = aDecoder.decodeObjectForKey("name") as! String self.url = aDecoder.decodeObjectForKey("url") as! String self.desc = aDecoder.decodeObjectForKey("desc") as! String } func encodeWithCoder(aCoder: NSCoder) { aCoder.encodeObject(self.name, forKey: "name") aCoder.encodeObject(self.url, forKey: "url") aCoder.encodeObject(self.desc, forKey: "desc") } }

2) Para guardar datos use la siguiente función:

func saveData() { let data = NSKeyedArchiver.archivedDataWithRootObject(custom) let defaults = NSUserDefaults.standardUserDefaults() defaults.setObject(data, forKey:"customArray" ) }

3) Para recuperar:

if let data = NSUserDefaults.standardUserDefaults().objectForKey("customArray") as? NSData { custom = NSKeyedUnarchiver.unarchiveObjectWithData(data) as! [customData] }

Nota: Aquí estoy guardando y recuperando una matriz de los objetos de clase personalizados.


Swift 3

class MyObject: NSObject, NSCoding { let name : String let url : String let desc : String init(tuple : (String,String,String)){ self.name = tuple.0 self.url = tuple.1 self.desc = tuple.2 } func getName() -> String { return name } func getURL() -> String{ return url } func getDescription() -> String { return desc } func getTuple() -> (String, String, String) { return (self.name,self.url,self.desc) } required init(coder aDecoder: NSCoder) { self.name = aDecoder.decodeObject(forKey: "name") as? String ?? "" self.url = aDecoder.decodeObject(forKey: "url") as? String ?? "" self.desc = aDecoder.decodeObject(forKey: "desc") as? String ?? "" } func encode(with aCoder: NSCoder) { aCoder.encode(self.name, forKey: "name") aCoder.encode(self.url, forKey: "url") aCoder.encode(self.desc, forKey: "desc") } }

para almacenar y recuperar:

func save() { let data = NSKeyedArchiver.archivedData(withRootObject: object) UserDefaults.standard.set(data, forKey:"customData" ) } func get() -> MyObject? { guard let data = UserDefaults.standard.object(forKey: "customData") as? Data else { return nil } return NSKeyedUnarchiver.unarchiveObject(with: data) as? MyObject }