[英]Storing NSMutableArray filled with custom objects in NSUserDefaults - crash
EDIT: Here is a working version. 编辑:这是一个工作版本。 I was able to retrieve my object within the NSMUtableArray after I saved and loaded it from the NSUserDefaults via NSCoding. 保存并通过NSCoding从NSUserDefaults加载对象后,便可以在NSMUtableArray中检索对象。 I think it's important to mention, that you not only need to de-archive the array, but also all its content. 我认为必须提及的是,您不仅需要解压缩数组,还需要解压缩其所有内容。 As you can see, I had to not only store the NSData of my freeze object, but also the NSData of my array: 如您所见,我不仅必须存储冻结对象的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];
}
EDIT: This is an error I got before, it doesn't show up anymore with the updated version above. 编辑:这是我之前收到的错误,上面的更新版本不再显示。
It crashes at the very end and the debugger reveals the following error: * * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSConcreteMutableData InitString]: unrecognized selector sent to instance 0x6b1fe20'* 它最终崩溃,并且调试器显示以下错误: * *由于未捕获的异常'NSInvalidArgumentException'而终止应用程序,原因:'-[NSConcreteMutableData InitString]:无法识别的选择器已发送至实例0x6b1fe20'*
Furthermore, it says that "NewFreeze" is not of type CFString. 此外,它说“ NewFreeze”不是CFString类型。 Does anybody have a clue what's going on? 有人知道发生了什么吗? I really want to save my Objects like that. 我真的很想这样保存我的对象。
The problem is that storing custom classes as a node in the Settings plist (NSUserDefaults) is not allowed, since the data is stored in the file system rather than an object from the application. 问题在于,不允许将自定义类存储为Settings plist(NSUserDefaults)中的节点,因为数据存储在文件系统中,而不是应用程序中的对象。 The settings app (where this data will also be visible) has no idea what a "Freeze" object is. 设置应用程序(此数据也将在其中可见)不知道什么是“冻结”对象。 The best way to handle what you want to do is to probably use Core Data. 处理您要执行的操作的最佳方法可能是使用Core Data。
also of note: the error you are getting is caused at the end of the method when you try to initalize a new Freeze
object from your runes
array, because when you put the object into the runes array you encapsulated it as a NSData
object first, but when you get it out you don't dearchive it before setting it to NewFreeze
还要注意:当您尝试从runes
数组中初始化一个新的Freeze
对象时,在方法末尾会导致错误,因为将对象放入runes数组时,首先将其封装为NSData
对象,但是当你得到它时,不要在将其设置为NewFreeze
之前对其进行NewFreeze
You certainly can save your own objects using the NSKeyedArchiver
class like you want to. 当然,您可以使用NSKeyedArchiver
类来保存自己的对象。 But: 但:
- (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;
}
And like it was mentioned before, you can't save it to NSUserDefaults. 而且就像之前提到的那样,您无法将其保存到NSUserDefaults。 You can save it to a file in the application documents directory, though. 不过,您可以将其保存到应用程序文档目录中的文件中。
EDIT: here is some code that I wrote about a year ago as a test. 编辑:这是我一年前编写的一些代码,作为测试。 I think this might be very close to what your trying to do. 我认为这可能与您的尝试非常接近。 There's some stuff there that is not related, like loading a plist from the main bundle, but I'll leave it in just in case someone might find it useful. 那里有些东西不相关,例如从主捆绑包中加载一个plist,但是我会保留它,以防万一有人发现它有用。
HighScoresManager.h 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 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
Maybe I should have just uploaded the files somewhere and linked them here... anyway, it's done now. 也许我应该将文件上传到某个地方,并在此处链接它们……无论如何,现在就完成了。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.