5

我正在为 iPhone 应用程序编写一些代码,但在正确加载默认数据时遇到问题。我的代码基于 Ray Wenderlich 的“Learning Cocos2d”一书中的一些示例。

似乎即使当我彻底删除应用程序并尝试从新数据开始时,应用程序也不一致地不尝试加载数据,或者错误地认为有数据并加载 null。

我正在使用 containsValueForKey 检查值是否存在,然后加载它或加载一些默认值,但即使在全新安装时, containsValueForKey 也会找到数据并且不加载默认值。在 xcode 的管理器中,我检查了我设备的文件结构和我指定保存的 Documents 文件夹,它看起来不包含任何文件,所以我不确定它在抓取什么。

我的猜测是问题与 initWithCoder 函数有关。它有时似乎神秘地通过了该功能,但并非一直如此。另一个奇怪的事情是,[[GameManager sharedGameManager] save]当玩家获得高分时我会调用(此处未显示,但代码与此objectiveList 完全相同,只是一个int)并且它似乎正确保存了它。

现在代码:

GC数据库.h

#import <Foundation/Foundation.h>
id loadData(NSString * filename);
void saveData(id theData, NSString *filename);

GC数据库.m

#import "GCDatabase.h"
NSString * pathForFile(NSString *filename) {
// 1
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
// 2
NSString *documentsDirectory = [paths objectAtIndex:0];
// 3
return [documentsDirectory stringByAppendingPathComponent:filename];
}

id loadData(NSString * filename) {

NSString *filePath = pathForFile(filename);
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {

    NSData *data = [[[NSData alloc] initWithContentsOfFile:filePath] autorelease];
    NSKeyedUnarchiver *unarchiver = [[[NSKeyedUnarchiver alloc] initForReadingWithData:data] autorelease];
    id retval = [unarchiver decodeObjectForKey:@"Data"];
    [unarchiver finishDecoding];
    return retval;
}
return nil;
}

void saveData(id theData, NSString *filename) {

NSMutableData *data = [[[NSMutableData alloc] init] autorelease];
NSKeyedArchiver *archiver = [[[NSKeyedArchiver alloc] initForWritingWithMutableData:data] autorelease];
[archiver encodeObject:theData forKey:@"Data"];
[archiver finishEncoding];
[data writeToFile:pathForFile(filename) atomically:YES];
}

游戏管理器.h

@interface GameManager : NSObject <NSCoding>{
NSMutableArray *objectiveDescriptions;
}
@property (nonatomic, retain) NSMutableArray * objectiveDescriptions;
+(GameManager*)sharedGameManager;
-(void)save;
-(void)load;
-(void)encodeWithCoder:(NSCoder *)encoder;
-(id)initWithCoder:(NSCoder *)decoder;

@end

GameManager.m(我添加了加载功能,试图强制它加载,但它似乎不起作用)

+(GameManager*)sharedGameManager {
@synchronized([GameManager class])
{
    if(!sharedGameManager) {
        sharedGameManager = [loadData(@"GameManager") retain];
        if (!sharedGameManager) {
            [[self alloc] init];
        }
    }
    return sharedGameManager;
}
return nil; 
}

+(id)alloc {
@synchronized([GameManager class]){
    NSAssert(sharedGameManager == nil, @"Attempted to allocate a second instance of the Game Manager singleton");
    sharedGameManager = [super alloc];
    return sharedGameManager;
}
return nil;
}

- (void)dealloc {
[objectiveList release];
[super dealloc];
}

- (void)save {
saveData(self, @"GameManager");
}

-(void)load {
loadData(@"GameManager");
}

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

- (id)initWithCoder:(NSCoder *)decoder  {
self = [super init];
if (self != nil) {
if ([decoder containsValueForKey:@"objectiveList"]) {
        objectiveList = [decoder decodeObjectForKey:@"objectiveList"];
    } else {
        [objectiveList addObjectsFromArray:[NSArray arrayWithObjects:@"1",@"2",@"3",@"4",@"5", nil]];
    }
}

return self;
}
@end
4

4 回答 4

1

我还没有阅读您的完整代码..但是我发现代码中有问题....

您还没有为objectiveList数组分配内存。除非并且直到您为数组分配内存,否则不会添加对象...

我想去

 objectiveList = [[NSMutableArray alloc] initWithArray:[NSArray arrayWithObjects:@"1",@"2",@"3",@"4",@"5", nil]];

代替

    [objectiveList addObjectsFromArray:[NSArray arrayWithObjects:@"1",@"2",@"3",@"4",@"5", nil]];

检查语法..希望这可能会有所帮助,因为它在过去也困扰着我,我忘记为数组分配内存..并继续添加导致 null 的对象... :) 如果它不能解决您的问题问题,我稍后会完全寻找代码.. :)

于 2012-05-07T16:27:05.003 回答
1

我似乎看到了问题。第一次调用构造函数时,甚至没有创建objectiveList,因为从未调用过“initWithCoder”。您还必须重写 init 方法才能构造objectiveList 数组。基本上,调用 init 方法的代码在这里:

+(GameManager*)sharedGameManager {
@synchronized([GameManager class])
{
    if(!sharedGameManager) {
        sharedGameManager = [loadData(@"GameManager") retain];
        if (!sharedGameManager) {
            [[self alloc] init]; // GOES INTO INIT METHOD, NOT INITWITHCODER!
        }
    }
    return sharedGameManager;
}
return nil; 
}

另一方面,单例实现让我很头疼。只是说。:)

于 2012-05-09T17:13:10.113 回答
0

(据我从您提供的代码中可以看出)您的代码存在逻辑缺陷。考虑如果decoder不包含objectiveList密钥会发生什么;该else子句将执行,但您从未分配过objectiveList,因此addObjectsFromArray:调用将静默失败。

要测试这个理论,请按如下所示更改您的代码,然后重新运行。如果断言触发了,那么上面的理论是正确的,如果不是,你需要多寻找一点!


- (id)initWithCoder:(NSCoder *)decoder
{ self = [super init]; if (self != nil) { if ([decoder containsValueForKey:@"objectiveList"]) { objectiveList = [decoder decodeObjectForKey:@"objectiveList"]; } else { NSAssert(objectiveList, @"objectiveList must be non-nil to add objects."); [objectiveList addObjectsFromArray[NSArrayarrayWithObjects:@"1",@"2",@"3",@"4",@"5", nil]]; } } return self; }

顺便说一句,objectiveList从未将其声明为 ivar ......我有点假设objectiveList并且objectiveDescriptions是相同的。

于 2012-05-12T08:30:01.267 回答
0

GameManager.m 中的方法应如下所示:

- (id)initWithCoder:(NSCoder *)decoder  {
self = [super init];
if (self != nil) {
if ([decoder containsValueForKey:@"objectiveList"]) {
        objectiveList = [[decoder decodeObjectForKey:@"objectiveList"] retain];
    } else {
        objectiveList = [[NSMutableArray alloc] initWithObjects:@"1",@"2",@"3",@"4",@"5", nil];
    }
}

您有两种情况:要么objectiveList存在,在这种情况下您之前保存了一些数据,要么不存在,您需要创建默认数据(1、2、3、4、5)。在上面的代码中,我更改了第一种情况以保留由 返回的数组decodeObjectForKey,因为 Apple 的文档声明此方法返回一个autorelease对象。您需要在此处保留它,以防止内存被稍后在您的应用程序中创建的其他一些对象重用。通过不保留objectiveList,稍后访问它时,您可能访问的是垃圾结果(即随机内存),而不是您刚刚解码的内容。

在类似的说明中,在不存在的第二种情况下objectiveList- 即对于没有保存数据的应用程序的新安装 - 您objectiveList在尝试向其添加对象之前没有分配。我已将此行更改为实际alloc的对象(因此需要的内存),然后init使用您想要的默认值。由于您之前尝试将项目添加到尚未创建的数组中,因此在尝试从中访问值时会再次获得垃圾数据。请注意,我假设您在NSMutableArray这里使用的是 an,但您也可能使用的是NSMutableSet.

于 2012-05-14T09:23:26.400 回答