我的应用程序运行良好,直到我停止并重新启动 - 因此存档文件 - highScores.archive存在。然后,应用程序无法编码 - 我在第一行得到一个EXC_BAD_ACCESS(很长一段时间,直到我到达我正在编码的日期对象时才发生。
我的猜测是我需要在几个地方保留,但我不知道在哪里。
代码:
FlipHighScores.h
...
@interface FlipHighScores : NSObject <NSCoding> {
//NSString *themeChosen;
NSInteger newHighScore;
NSInteger newScoreStartLevel;
NSInteger newScoreFinishLevel;
NSDate *scoreDateCreated;}
@property (copy, nonatomic) NSString *themeChosen;
@property (nonatomic) NSInteger highScore;
@property (nonatomic) NSInteger scoreStartLevel;
@property (nonatomic) NSInteger scoreFinishLevel;
@property (nonatomic, readonly, strong) NSDate *scoreDateCreated;
...
FlipHighScores.m ...
@synthesize themeChosen = _themeChosen;
@synthesize highScore = _highScore;
@synthesize scoreStartLevel = _scoreStartLevel;
@synthesize scoreFinishLevel = _scoreFinishLevel;
@synthesize scoreDateCreated = _scoreDateCreated;
...
-(void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:_themeChosen forKey:@"_themeChosen"];
NSLog(@"Theme Chosen is %@", _themeChosen);
[aCoder encodeInt:_highScore forKey:@"_highScore"];
[aCoder encodeInt:_scoreStartLevel forKey:@"_scoreStartLevel"];
[aCoder encodeInt:_scoreFinishLevel forKey:@"_scoreFinishLevel"];
NSLog(@"Date Created in encodeWithCoder is %@", _scoreDateCreated);
[aCoder encodeObject:_scoreDateCreated forKey:@"_scoreDateCreated"];}
-(id)initWithCoder:(NSCoder *)aDecoder {
if (self) {
_themeChosen = [aDecoder decodeObjectForKey:@"_themeChosen"];
_highScore = [aDecoder decodeIntForKey:@"_highScore"];
_scoreStartLevel = [aDecoder decodeIntForKey:@"_scoreStartLevel"];
_scoreFinishLevel = [aDecoder decodeIntForKey:@"_scoreFinishLevel"];
_scoreDateCreated = [aDecoder decodeObjectForKey:@"_scoreDateCreated"];
}
return self;}
-(NSString *)description {
NSDate *date = _scoreDateCreated;
NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init] autorelease];
[dateFormatter setDateStyle:NSDateFormatterShortStyle];
NSString *dateString = [dateFormatter stringFromDate:date];
//NSLog(@"dateString from description is %@", dateString);
NSString *descriptionString = [[NSString alloc] initWithFormat:@"%d %@ S:%d F:%d D:%@", _highScore, _themeChosen, _scoreStartLevel, _scoreFinishLevel, dateString];
return descriptionString:}
我觉得令人困惑的是,如果我删除保存文件 - highScores.archive并运行应用程序,它运行没有问题。我停止并杀死应用程序,然后再次启动它 - 第一次调用编码时崩溃。
在我编码themeChosen对象的行。我已经阅读了一些关于解码问题的帖子,这些问题通过“保留”或更改来修复。格式(为什么这会有所帮助,我真的不明白)。但这是编码。解码可能是下一个问题......
我没有在这个项目上使用ARC。也许当我从头开始重建整个事情时......
哦,我忘了提到我测试的所有内容都运行顺畅,直到我添加了跟踪Theme变量。然后事情就像这里提到的那样微不足道。
答案 0 :(得分:4)
我认为您的问题出在-initWithCoder
。您正在获取-decodeObjectForKey:
的结果并直接将其分配给合成的ivar。通常假定名称中没有“copy”一词的方法返回自动释放的对象。
如果你直接将一个自动释放的对象分配给一个变量,那么该对象将在下一个运行循环中释放,将自行释放,现在你的变量指向垃圾内存。当您尝试访问它时,您将获得exec_bad_access
。
应该正在做什么是利用@synthesize
为您创建的访问方法。而不是
_themeChosen = [aDecoder decodeObjectForKey:@"_themeChosen"];
你应该写
[self setThemeChosen:[aDecoder decodeObjectForKey:@"_themeChosen"]];
或者,如果你肯定必须使用等号,你可以使用“点符号”的语法糖:
self.themeChosen = [aDecoder decodeObjectForKey:@"_themeChosen"]
最终会将其转化为接近同一事物。
关键是:合成的setter不仅仅是将对象分配给ivar。它还保留了对象(实际上,在这种情况下,它复制对象,因为您在copy
声明中指定了@property
。这只是众多,许多原因中的一个,你永远不应该直接访问ivars并且总是使用访问者 - 特别是现在他们基本上是为你自动编写的@property
/ @synthesize
。
<强>更新强>
当您在scoreDateCreated
声明中将readonly
声明为@property
时,您会发现@property
遇到问题。为什么它只读?它似乎不是派生值,所以你显然必须为它分配一些东西。
如果您希望在对象中读/写,但只希望公开只读接口,则可以在FlipHighScores.m
顶部的匿名类别中将{{1}}重新声明为读/写。所以看起来只读取包含标题的任何内容,但实际上在对象的实现中读/写。