在 plist 中更新和保存数据

Posted

技术标签:

【中文标题】在 plist 中更新和保存数据【英文标题】:Updating and saving data in plist 【发布时间】:2012-09-05 04:33:16 【问题描述】:

我正在制作一款 ios 游戏,需要保存玩家已达到的***别。我可以成功更改 plist 中的数据元素,但由于某种原因,每次游戏重新启动时,该数据都会恢复到其原始值。这是我的代码的基本流程:

在游戏的init中,获取玩家达到的最高等级(原值为1)

pData = [[PlayerData alloc] init];
currentLevel = [[pData.data valueForKey:@"Highest Level"] intValue];
[self startNewLevel:currentLevel];

'data' 是一个 NSMutableDictionary,它在 PlayerData 中像这样初始化:

self.data = [NSMutableDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"playerdata" ofType:@"plist"]];

稍后,如果玩家击败***别,我会增加“***别”的值并通过在 PlayerData 中调用此函数来写入文件:

-(void) newHighestLevel:(NSString*)path :(int)level
     [self.data setValue:[NSNumber numberWithInt:level] forKey:path];
     [self.data writeToFile:@"playerdata.plist" atomically:YES]

我知道这一切都有效,因为我有一个玩家可以在玩游戏时访问的关卡菜单。每次玩家按下级别菜单按钮时,都会创建一个 UITableView 的子类,显示级别 1 到达到的***别。初始化如下所示:

levelMenu = [[LevelMenu alloc] init:[[pData.data valueForKey:@"Highest Level"] intValue]];

关卡菜单在游戏中显示正确的关卡数量(例如,如果玩家没有通过任何关卡并进入关卡菜单,它只会显示“1 级”;但如果玩家通过了 1 级并且进入级别菜单,显示“级别 1”和“级别 2”)。但是,每当应用程序终止或用户退出游戏主菜单时,“***别”的值将恢复为 1,并且此代码:

currentLevel = [[pData.data valueForKey:@"Highest Level"] intValue];

当玩家按下开始时,始终将 currentLevel 设置为 1,无论用户在玩游戏时通过了多少关卡。

为什么值变回来了?我是否遗漏了一些会使我的 plist 编辑永久化的内容?

编辑:

这是我的新 'newHighestLevel' 方法:

-(void) newHighestLevel:(NSString*)path :(int)level
    [self.data setValue:[NSNumber numberWithInt:level] forKey:path];
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
    NSString *docfilePath = [basePath stringByAppendingPathComponent:@"playerdata.plist"];
    [self.data writeToFile:docfilePath atomically:YES];
    self.data = [NSMutableDictionary dictionaryWithContentsOfFile:docfilePath];
    BOOL write = [self.data writeToFile:docfilePath atomically:YES];

write 设置为 YES。如果我将 'docfilePath' 更改为 @"playerdata.plist",它将被设置为 NO。在这两种情况下,游戏似乎都没有改变。

解决方案:

-(void) newHighestLevel:(NSString*)path :(int)level
      [self.data setValue:[NSNumber numberWithInt:level] forKey:path];
      NSString *basePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
      NSString *docfilePath = [basePath stringByAppendingPathComponent:@"playerdata.plist"];
      [self.data writeToFile:docfilePath atomically:YES];

在初始化中

-(id) init

     NSString *basePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
     NSString *docfilePath = [basePath stringByAppendingPathComponent:@"playerdata.plist"];
     NSFileManager *fileManager = [NSFileManager defaultManager];
     if (![fileManager fileExistsAtPath:docfilePath])
         NSString *sourcePath = [[NSBundle mainBundle] pathForResource:@"playerdata" ofType:@"plist"];
         [fileManager copyItemAtPath:sourcePath toPath:docfilePath error:nil];
     
     self.data = [NSMutableDictionary dictionaryWithContentsOfFile:docfilePath];
     return self;

【问题讨论】:

如果您希望所有与您一起工作的开发人员永远讨厌您并诅咒您的名字,请继续在您的方法签名中使用未命名的参数:) 至于您的问题,[self.data writeToFile:@"playerdata.plist" atomically:YES] 调用返回什么值?我怀疑这是一个只读文件,写入失败,但您没有发现错误。 它返回 NO。有没有办法使 plist 可写?还是其他非只读的数据文件? 我认为普林斯的回答应该适合你。 如果您只是存储已完成的***别,则可以将其存储在NSUserDefaults 【参考方案1】:

从 plist 创建字典的最简单方法是使用方法 dictionaryWithContentsOfFile:,

例如,从您的资源中加载一个 plist:

NSString *filePath = [[NSBundle mainBundle] pathForResource:@"playerData" ofType:@"plist"];
NSMutableDictionary *plistdict = [NSMutableDictionary dictionaryWithContentsOfFile:filePath];

编写 plistdict 同样简单:

[plistdict writeToFile:filePath atomically:YES];

注意,您无法写入应用的资源,因此您必须创建不同的路径,例如在您的 plist 的 Documents 目录中。

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
NSString *docfilePath = [basePath stringByAppendingPathComponent:@"playerData.plist"];
[plistdict writeToFile:docfilePath atomically:YES];

现在再次从 plist 中检索数据。

 NSMutableDictionary *plistdict = [NSMutableDictionary dictionaryWithContentsOfFile:docfilePath];

在 plistDict 中添加您的内容并重新编写。

【讨论】:

我尝试了您的解决方案,最后一次 plist 写入返回 true,但问题没有解决 - 游戏在重新启动时仍然恢复到 1 级。 @CBas 加载的是同一个文件,而不是你的包中的那个? iPad 已断开连接,它没有重新加载捆绑包.. 你的意思是我需要更改我的 init 吗? 我误解了您的评论 - 从新文件路径加载解决了问题。虽然我不需要 basePath 部分,但我在上面发布了工作代码【参考方案2】:

方法writeToFile:atomically 返回BOOL 结果。我猜该文件根本没有被保存。

您的游戏正常运行的原因是应用程序从Dictionary(启动时填充一次)中读取数据,而不是每次都直接从plist 文件中读取数据(也不应该)。

查看writeToFile:atomically方法的返回值,判断文件是否真正被保存。

【讨论】:

它返回 NO。还有其他方法可以写入文件吗? ***.com/questions/2010192/…

以上是关于在 plist 中更新和保存数据的主要内容,如果未能解决你的问题,请参考以下文章

writeToFile:atomically: 不保存新的 plist 数据

如何将本地保存的 xml 文件保存到 .plist 文件中

保存数据 - 核心数据与 plist 文件

将数据保存在 plist 中并访问此数据

将数据保存到每个对象或人的 plist

转基本数据持久性 使用plist保存和读取数据