Save your own class using NSCoder

I am trying to save some custom classes / data to a file in an iPhone / iPad application.

I have an RSHighscoreList class

@interface RSHighscoreList : NSObject { NSMutableArray *list; } 

which contains the RSHighscore objects in the list

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

When I try to save everything to a file

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

Application crashes with error message

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

I wonder why the error speaks of RSHighscore, even if it is "wrapped". Anyone have a good idea?

+4
source share
2 answers

RSDataStore has the -encodeWithCoder: method, but (according to the error message) RSHighscore not. You need to implement the NSCoding protocol for each class that you serialize.

 @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]; } ... 

If the RSHighscore base class RSHighscore ever been changed to something other than NSObject , you may need to change the -initWithCoder: method to call [super initWithCoder:decoder] rather than [super init] . Alternatively, add <NSCoding> to NSObject and change RSHighscore -initWithCoder: now.

 @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; } ... 
+10
source

The class you are going to encode or initWithCoder must conform to the <NSCoding> protocol <NSCoding> Therefore, you just need to add this to your interface, otherwise the run time will not recognize the selector, since it is part of the <NSCoding> protocol

+4
source

All Articles