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.