Come archiviare oggetti personalizzati in NSUserDefaults


268

Bene, quindi ho fatto un po 'di frugate in giro, e mi rendo conto del mio problema, ma non so come risolverlo. Ho creato una classe personalizzata per contenere alcuni dati. Realizzo oggetti per questa classe e ho bisogno che durino tra le sessioni. Prima di inserire tutte le mie informazioni NSUserDefaults, ma non funziona.

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

Questo è il messaggio di errore che visualizzo quando inserisco la mia classe personalizzata "Player" in NSUserDefaults. Ora, ho letto che apparentemente NSUserDefaultsmemorizza solo alcuni tipi di informazioni. Quindi come posso ottenere i miei oggetti NSUSerDefaults?

Ho letto che dovrebbe esserci un modo per "codificare" il mio oggetto personalizzato e poi inserirlo, ma non sono sicuro di come implementarlo, l'aiuto sarebbe apprezzato! Grazie!

****MODIFICARE****

Bene, quindi ho lavorato con il codice indicato di seguito (grazie!), Ma sto ancora riscontrando alcuni problemi. Fondamentalmente, il codice si arresta in modo anomalo ora e non sono sicuro del perché, perché non fornisce alcun errore. Forse mi manca qualcosa di essenziale e sono troppo stanco, ma vedremo. Ecco l'implementazione della mia classe personalizzata, "Player":

@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

File di implementazione:

#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

Questa è la mia classe, piuttosto semplice, so che funziona nel realizzare i miei oggetti. Quindi, ecco le parti rilevanti del file AppDelegate (dove chiamo le funzioni di crittografia e decrittografia):

@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

E poi le parti importanti del file di implementazione:

    #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, scusa per tutto il codice. Sto solo cercando di aiutare. Fondamentalmente, l'app si avvierà e poi si arresterà immediatamente. L'ho ridotto alla parte di crittografia dell'app, è lì che si blocca, quindi sto facendo qualcosa di sbagliato ma non sono sicuro di cosa. L'aiuto sarebbe di nuovo apprezzato, grazie!

(Non sono ancora riuscito a decifrare, poiché non ho ancora funzionato la crittografia.)


Hai una traccia dello stack o ulteriori informazioni sull'incidente, ad esempio quale numero di riga sta causando l'incidente? Non vedo immediatamente nulla di sbagliato nel codice, quindi un punto di partenza sarebbe utile.
— chrissr,

Nell'esempio sopra hai usato encodeObject per memorizzare self.life che è un int. Si dovrebbe usare invece encodeInt.
— broot

Risposte:


516

Nella tua classe Player, implementa i due metodi seguenti (sostituendo le chiamate a encodeObject con qualcosa di rilevante per il tuo oggetto):

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

Leggere e scrivere da 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;
}

Codice preso spudoratamente in prestito da: classe di salvataggio in nsuserdefaults


Ho modificato il mio post sopra per riflettere le mie modifiche.
— Ethan Mick,

@chrissr hai un errore nelle impostazioni predefinite di NSUserDefaults = [NSUserDefaults standardUserDefaults]; ... dovrebbero essere i valori predefiniti di NSUserDefaults *.
— Maggie,

2
NSKeyedArchiver oscilla ... sembra addirittura scendere automaticamente negli oggetti NSArray o NSDictionary e codificare qualsiasi oggetto personalizzato codificabile all'interno.
— BadPirate,

2
Non sono pedante, solo una vera domanda, non è contro le linee guida sulle mele usare setter sintetizzati, cioè l'auto-proprietà nei metodi init?
— Samhan Salahuddin,

2
@chrissr Per favore cambia NSData *myEncodedObject = [NSKeyedArchiver archivedDataWithRootObject:object];per NSData *encodedObject = [NSKeyedArchiver archivedDataWithRootObject:object];. Le variabili non corrispondono.
— GoRoS,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.