Хранение NSMutableArray, заполненного пользовательскими объектами в NSUserDefaults - сбой - PullRequest
0 голосов
/ 16 ноября 2010

РЕДАКТИРОВАТЬ: Вот рабочая версия.Мне удалось получить свой объект в NSMUtableArray после того, как я сохранил и загрузил его из NSUserDefaults через NSCoding.Я думаю, что важно упомянуть, что вам нужно не только деархивировать массив, но и весь его контент.Как вы можете видеть, мне нужно было не только сохранить NSData моего объекта замораживания, но также и NSData моего массива:

// My class "Freeze"
@interface Freeze : NSObject <NSCoding> // The NSCoding-protocoll is important!!
{
    NSMutableString *name;
}
@property(nonatomic, copy) NSMutableString *name;

-(void) InitString;
@end

@implementation Freeze
@synthesize name;

-(void) InitString {
    name = [[NSMutableString stringWithString:@"Some sentence... lalala"] retain];
}

// Method from NSCoding-protocol
- (void)encodeWithCoder:(NSCoder *)encoder
{
    //Encode properties, other class variables, etc
    [encoder encodeObject:self.name forKey:@"name"];

}

// Method from NSCoding-protocol
- (id)initWithCoder:(NSCoder *)decoder
{
    self = [super init];
    if( self != nil )
    {
        //decode properties, other class vars
        self.name = [decoder decodeObjectForKey:@"name"];

    }
    return self;
}
@end




Freeze *freeze;
NSMutableArray *runes;
NSMutableArray *newRunes;


runes = [[NSMutableArray alloc] init];
newRunes = [[NSMutableArray alloc] init];

freeze = [[Freeze alloc] init];
[freeze InitString];
[runes addObject:freeze];

[self saveState];
[self restoreState];

Freeze *newFreeze = [[Freeze alloc] init];
newFreeze = [newRunes objectAtIndex:0];
NSString *String = [NSString stringWithString:newFreeze.name];
NSLog(@"%@", String);

//-----------------------------------------------------------------------------

- (void) saveState 
{    
    NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];

    NSData* myClassData = [NSKeyedArchiver archivedDataWithRootObject:freeze];
    [defaults setObject:myClassData forKey:@"MyClass"];

    NSData* myClassArrayData = [NSKeyedArchiver archivedDataWithRootObject:runes];
    [defaults setObject:myClassArrayData forKey:@"MyClassArray"];

}

- (void) restoreState 
{    
    NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];

    NSData* myClassData = [defaults objectForKey:@"MyClass"];
    freeze = [NSKeyedUnarchiver unarchiveObjectWithData:myClassData];

    NSData* myClassArrayData = [defaults objectForKey:@"MyClassArray"];    
    NSArray *savedMyClassArray = [NSKeyedUnarchiver unarchiveObjectWithData:myClassArrayData];
    if( savedMyClassArray != nil ) 
        newRunes = [[NSMutableArray alloc] initWithArray:savedMyClassArray];    
    else
    newRunes =  [[NSMutableArray alloc] init];
}

РЕДАКТИРОВАТЬ: это ошибка, которую я получил раньше, она не показываетбольше с обновленной версией выше.

Сбой в самом конце, и отладчик выдает следующую ошибку: ** Завершение приложения из-за необработанного исключения 'NSInvalidArgumentException', причина: '-[NSConcreteMutableData InitString]: нераспознанный селектор, отправленный экземпляру 0x6b1fe20 '*

Кроме того, он говорит, что «NewFreeze» не относится к типу CFString.Кто-нибудь знает, что происходит?Я действительно хочу сохранить мои объекты, как это.

Ответы [ 2 ]

1 голос
/ 16 ноября 2010

Проблема заключается в том, что сохранение пользовательских классов в виде узла в списке параметров (NSUserDefaults) недопустимо, поскольку данные хранятся в файловой системе, а не в объекте из приложения. Приложение настроек (где эти данные также будут видны) понятия не имеет, что такое объект «Freeze». Лучший способ справиться с тем, что вы хотите сделать, - это использовать Core Data.

также примечание: ошибка, которую вы получаете, возникает в конце метода, когда вы пытаетесь инициализировать новый объект Freeze из массива runes, потому что когда вы помещаете объект в массив runes, который вы инкапсулировали сначала это NSData объект, но когда вы получаете его, вы не разархивируете его, пока не установите NewFreeze

0 голосов
/ 16 ноября 2010

Вы, конечно, можете сохранять свои собственные объекты, используя класс NSKeyedArchiver, как хотите. Но:

- (id)initWithCoder:(NSCoder *)decoder
{
    //self = [[Freeze alloc] init]; // I don't think you want to
                                    // allocate a new object here,
    self = [super init];
    if( self != nil )
    {
        //decode properties, other class vars
        self.name = [decoder decodeObjectForKey:@"name"];

    }
    return self;
}

И, как упоминалось ранее, вы не можете сохранить его в NSUserDefaults. Вы можете сохранить его в файл в каталоге документов приложения.

РЕДАКТИРОВАТЬ: вот код, который я написал около года назад в качестве теста. Я думаю, это может быть очень близко к тому, что вы пытаетесь сделать. Там есть кое-что, что не связано, например, загрузка plist из основного пакета, но я оставлю это на всякий случай, если кто-то найдет это полезным.

HighScoresManager.h

#import <Foundation/Foundation.h>

@interface ScoreEntry : NSObject <NSCoding> 
{
    NSString *player;
    NSInteger score;
}
@property (nonatomic, retain) NSString *player;
@property (nonatomic, assign) NSInteger score;

+ (ScoreEntry *) entryWithScore:(NSInteger) score andPlayer:(NSString *)player;

@end


@interface HighScoresManager : NSObject
{
    NSMutableArray *m_Highscores;
}
@property (nonatomic, readonly) NSArray *highscores;

- (void) addScore: (NSInteger) score forPlayer:(NSString *)player;
- (void) addScoreEntry: (ScoreEntry *) entry;

@end

HighScoresManager.m

#import "HighScoresManager.h"

@implementation ScoreEntry
@synthesize player;
@synthesize score;

+ (ScoreEntry *) entryWithScore:(NSInteger) score andPlayer:(NSString *)player
{
    ScoreEntry *entry = [[[ScoreEntry alloc] init] autorelease];
    entry.score = score;
    entry.player = player;

    return entry;
}

+ (ScoreEntry *) entryWithNSDictionary:(NSDictionary *)dict
{
    ScoreEntry *entry = [[[ScoreEntry alloc] init] autorelease];
    entry.score = [[dict objectForKey:@"score"] intValue];
    entry.player = [dict objectForKey:@"player"];

    return entry;
}

- (void) encodeWithCoder: (NSCoder *) coder
{
    [coder encodeInteger:score forKey:@"score"];
    [coder encodeObject:player forKey:@"player"];
}

- (id) initWithCoder: (NSCoder *) decoder
{
    if( self = [super init] )
    {
        score = [decoder decodeIntForKey:@"score"];
        player = [[decoder decodeObjectForKey:@"player"] retain];
}

    return (self);
}

- (void) dealloc
{
    [player release];

    [super dealloc];
}

@end


@implementation HighScoresManager

- (BOOL) saveHighScores
{
    BOOL success = NO;

    if( m_Highscores )
    {
        NSArray *pathList = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, NO);

        NSString *path = [[[pathList objectAtIndex:0] stringByAppendingPathComponent:@"highscores.txt"] stringByExpandingTildeInPath];

        success = [NSKeyedArchiver archiveRootObject:m_Highscores toFile:path];
    }

    return success;
}

- (BOOL) loadHighScores
{
    BOOL success = NO;

    NSArray *pathList = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, NO);

    NSString *path = [[[pathList objectAtIndex:0] stringByAppendingPathComponent:@"highscores.txt"] stringByExpandingTildeInPath];

    if(m_Highscores)
    {
        [m_Highscores release];
    }

    m_Highscores = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
    if( m_Highscores )
    {
        [m_Highscores retain];
        success = YES;
    }
    else
    {
        m_Highscores = [[NSMutableArray alloc] init];
        success = NO;
    }

    // if empty, load default highscores from plist
    if([m_Highscores count] == 0)
    {
        NSString *resourcePath = [[NSBundle mainBundle] resourcePath];

        NSString *path = [[resourcePath stringByAppendingPathComponent:@"DefaultHighscores.plist"] stringByExpandingTildeInPath];

        NSDictionary *defaultHighscoresDict = [[NSDictionary alloc] initWithContentsOfFile:path];

        NSArray *defaultHighscores = [defaultHighscoresDict objectForKey:@"highscores"];

        for (NSDictionary *dict in defaultHighscores)
        {
            [m_Highscores addObject:[ScoreEntry entryWithNSDictionary:dict]];
        }

        [defaultHighscoresDict release];
    }

    return success;
}

- (id) init
{
    self = [super init];
    if(self)
    {
        [self loadHighScores];
    }
    return self;
}

- (NSArray *) highscores
{
    return (NSArray *) m_Highscores;
}

- (void) addScore: (NSInteger) score forPlayer:(NSString *)player
{
    [self addScoreEntry:[ScoreEntry entryWithScore:score andPlayer:player]];
}

- (void) addScoreEntry: (ScoreEntry *) entry
{
    [m_Highscores addObject:entry];
}

- (void) dealloc
{
    [self saveHighScores];
    [m_Highscores release];

    [super dealloc];
}

@end

Может быть, я должен был просто загрузить файлы куда-нибудь и связать их здесь ... в любом случае, это сделано сейчас.

...