我正在为iPhone应用程序编写一些代码,而且我遇到了正确加载默认数据的问题。我的代码基于Ray Wenderlich的“Learning Cocos2d”一书中的一些例子。
似乎即使我直接删除应用程序并尝试从新数据开始,应用程序不一致也不会尝试加载数据,或者错误地认为存在数据,并且加载为null。
我正在使用containsValueForKey来检查某个值是否存在,然后加载它或加载一些默认值,但即使在全新安装中,containsValueForKey也会查找数据并且不会加载默认值。在xcode的组织者中,我检查了我的设备的文件结构和我指定保存的Documents文件夹,看起来它不包含任何文件,所以我不确定它是什么。
我的猜测是问题与initWithCoder函数有关。它有时似乎神秘地经历了这个功能,但并非总是如此。另一个奇怪的事情是当玩家获得高分时我调用[[GameManager sharedGameManager] save]
(此处未显示,但代码与此objectiveList完全相同,只有一个int)并且它似乎正确保存。
现在代码:
GCDatabase.h
#import <Foundation/Foundation.h>
id loadData(NSString * filename);
void saveData(id theData, NSString *filename);
GCDatabase.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];
}
GameManager.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
答案 0 :(得分: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的对象... :) 如果它没有解决你的问题,我会在以后完全寻找代码......:)
答案 1 :(得分: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;
}
另一方面,单例实现给我带来了麻烦。只是说。 :)
答案 2 :(得分: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
是相同的。
答案 3 :(得分: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
,但您可能也在使用NSMutableSet
。