Сохранить собственный класс с NSCoder - PullRequest
4 голосов
/ 30 ноября 2010

Я пытаюсь сохранить некоторые пользовательские классы / данные в файл в приложении для iPhone / iPad.

У меня есть класс RSHighscoreList

@interface RSHighscoreList : NSObject {
    NSMutableArray *list;
}

, который содержит объекты RSHighscore всписок

@interface RSHighscore : NSObject {
    NSString *playerName;
    NSInteger points;
}

При попытке сохранить все в файл

- (void)writeDataStore {
    RSDataStore *tmpStore = [[RSDataStore alloc] init];
    _tmpStore.highscorelist = self.highscorelist.list;
    NSMutableData *data = [[NSMutableData alloc] init];
    NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];

    [archiver encodeObject:tmpStore forKey:kDataKey];
    [archiver finishEncoding];
    [data writeToFile:[self dataFilePath] atomically:YES];

    [archiver release];
    [data release];
}

@interface RSDataStore : NSObject <NSCoding, NSCopying> {
    NSMutableArray *highscorelist; 
}

- (void)encodeWithCoder:(NSCoder *)encoder {
    [encoder encodeObject:highscorelist forKey:@"Highscorelist"];
}

Приложение вылетит с сообщением об ошибке

-[RSHighscore encodeWithCoder:]: unrecognized selector sent to instance 0x573cc20
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[RSHighscore encodeWithCoder:]: unrecognized selector sent to instance 0x573cc20'

Интересно, почему ошибка говорит о RSHighscore, даже если она «завернута».У кого-нибудь есть хорошая идея?

Ответы [ 2 ]

10 голосов
/ 30 ноября 2010

RSDataStore имеет метод -encodeWithCoder:, но (согласно сообщению об ошибке) RSHighscore нет.Вам необходимо реализовать протокол NSCoding для каждого сериализуемого класса.

@implementation RSHighscore
static NSString *const kPlayerName = @"PlayerName";
static NSString *const kPoints = @"Points";

-(id)initWithCoder:(NSCoder *)decoder {
    if ((self=[super init])) {
        playerName = [[decoder decodeObjectForKey:kPlayerName] retain];
        points = [decoder decodeIntegerForKey:kPoints];
    }
    return self;
}
-(void)encodeWithCoder:(NSCoder *)encoder {
    [encoder encodeObject:playerName forKey:kPlayerName];
    [encoder encodeInt:points forKey:kPoints];
}
...

Если базовый класс RSHighscore когда-либо изменяется на что-то отличное от NSObject,-initWithCoder: метод, возможно, должен быть изменен для вызова [super initWithCoder:decoder], а не [super init].Или добавьте <NSCoding> к NSObject и измените RSHighscore s -initWithCoder: сейчас.

@interface NSObject (NSCoding)
-(id)initWithCoder:(NSCoder*)decoder;
-(void)encodeWithCoder:(NSCoder*)encoder;
@end

@implementation NSObject (NSCoding)
-(id)initWithCoder:(NSCoder*)decoder {
    return [self init];
}
-(void)encodeWithCoder:(NSCoder*)encoder {}
@end

@implementation RSHighscore
-(id)initWithCoder:(NSCoder *)decoder {
    if ((self=[super initWithCoder:decoder])) {
        playerName = [[decoder decodeObjectForKey:kPlayerName] retain];
        points = [decoder decodeIntegerForKey:kPoints];
    }
    return self;
}
...
4 голосов
/ 30 ноября 2010

Класс, который вы собираетесь кодировать или initWithCoder, должен соответствовать протоколу <NSCoding>. Поэтому вам просто нужно добавить это в ваш интерфейс, в противном случае среда выполнения не распознает селектор, поскольку он является частью протокола <NSCoding> * 1003.*

...