1

我的应用程序运行良好,直到我停止它并重新启动 - 于是存档文件 - 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。也许当我从头开始重建整个事情时......

哦,我忘了提到在我添加跟踪主题变量之前,一切都运行顺利,直到我测试过。然后事情变得有点不对劲,就像这里提到的那样。

4

1 回答 1

4

我认为您的问题出在您的-initWithCoder. 您正在获取结果-decodeObjectForKey:并将其直接分配给合成的 ivar。通常假定名称中没有“复制”一词的方法返回自动释放的对象。

如果您直接将自动释放的对象分配给变量,则该对象将在下一个运行循环中释放,将自行释放,现在您的变量指向垃圾内存。当您尝试访问它时,您将获得一个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。为什么是只读的?它似乎不是派生值,因此您显然必须为其分配一些东西。

如果您希望它在您的对象中读/写,但只想公开一个只读接口,您可以@propertyFlipHighScores.m. 因此,它看起来只对包含标头的任何内容都是只读的,但实际上在对象的实现中是读/写的。

于 2012-05-02T03:32:35.077 回答