【问题标题】:Saving OS X Game Data Using NSData and NSMutableArrays使用 NSData 和 NSMutableArrays 保存 OS X 游戏数据
【发布时间】:2015-06-27 19:01:14
【问题描述】:

我正在尝试将几个级别的高分存储在 NSMutableArray 中,然后将其保存在 Documents 文件夹中的文件中。我知道我可以使用 plist,但我不希望用户修改内容。 NSMutableArray *hiscore 似乎没有被初始化,我不知道如何解决这个问题。对于基元来说,这似乎很好,但对于对象,它就不起作用了。

GameData.h

@interface GameData : NSObject <NSCoding>

@property (assign, nonatomic) int level;
@property (assign, nonatomic) NSMutableArray *hiscore;

+(instancetype)sharedGameData;
-(void)save;
-(void)reset;

@end

GameData.m

#import "GameData.h"

@implementation GameData

static NSString* const GameDataLevelKey = @"level";
static NSString* const GameDataHiscoreKey = @"hiscore";

- (instancetype)initWithCoder:(NSCoder *)decoder {
    self = [self init];
    if (self) {
        _level = [decoder decodeDoubleForKey: GameDataLevelKey];
        _hiscore = [decoder decodeObjectForKey:GameDataHiscoreKey];
    }
    return self;
}

+ (instancetype)sharedGameData {
    static id sharedInstance = nil;

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [self loadInstance];
    });

    return sharedInstance;
}

+(instancetype)loadInstance {
    NSData* decodedData = [NSData dataWithContentsOfFile: [GameData filePath]];
    if (decodedData) {
        GameData* gameData = [NSKeyedUnarchiver unarchiveObjectWithData:decodedData];
        return gameData;
    }

    return [[GameData alloc] init];
}

-(void)encodeWithCoder:(NSCoder *)encoder {
    [encoder encodeDouble:self.level forKey: GameDataLevelKey];
    [encoder encodeObject:self.hiscore forKey:GameDataHiscoreKey];
}

+(NSString*)filePath {
    static NSString* filePath = nil;
    if (!filePath) {
        filePath =
        [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]
         stringByAppendingPathComponent:@"gamedata"];
    }
    return filePath;
}

-(void)save {
    NSData* encodedData = [NSKeyedArchiver archivedDataWithRootObject: self];
    [encodedData writeToFile:[GameData filePath] atomically:YES];
    /*[_hiscore writeToFile:[GameData filePath] atomically:YES];*/
}

-(void)reset {
    self.level = 0;
}

@end

LevelScene.m

#import "GameData.h"
...
[[[GameData sharedGameData] hiscore] addObject:@1500];
[[GameData sharedGameData] save];

【问题讨论】:

    标签: objective-c macos save


    【解决方案1】:

    几件事:

    首先,在initWithCoder: 中,如果已经没有游戏文件(就像第一次启动时的情况,或者如果保存时没有分数),_hiscore 将结束 nil,所以你赢了'无法为其添加任何分数。在调用decodeObjectForKey: 之后,我会检查它是否为零,如果它是零,则将其初始化为一个空数组。

    - (instancetype)initWithCoder:(NSCoder *)decoder {
        self = [self init];
        if (self) {
            _level = [decoder decodeDoubleForKey: GameDataLevelKey];
            _hiscore = [decoder decodeObjectForKey:GameDataHiscoreKey];
            if( _hiscore == nil ) {
                _hiscore = [NSMutableArray array];
            }
        }
        return self;
    }
    

    其次,GameData 拥有_hiscore,因此需要对其进行强引用。

    @property (strong, nonatomic) NSMutableArray *hiscore;
    

    编辑: 一般规则(参见Memory Management section of Apple's Cocoa Core Competencies Guide)是:

    您拥有通过为它分配内存或复制它创建的任何对象。

    这通常意味着您使用包含alloccopy 的方法创建的任何对象,但它也适用于array 等便捷方法。注意[NSMutableArray array] 完全等同于[[NSMutableArray alloc] init];使用前者的唯一原因是它减少了打字(包括我自己在内的许多人都喜欢)。

    那么,

    如果您拥有一个对象,无论是通过创建它还是表达所有权权益,您都有责任在不再需要它时释放它。

    在 ARC 之前,这意味着调用 releaseautorelease

    在 ARC 世界中,内存管理大部分由您负责,但您仍然需要了解其背后的概念才能使其有意义。基本上,如果您创建一个对象(使用包含alloccopy 的方法,或array 等便利方法),那么您就拥有该对象。您可以通过创建对该对象的strong 引用来表达该对象的所有权。如果您没有这样做(例如,您的参考是weakassign),那么 ARC 将(正确地)假设您已完成该对象,并在确定您完成后立即释放它它。在上面的代码中,ARC 选择在initWithCoder 返回后立即释放_hiscore 是一个安全的赌注。后来,你的代码尝试访问_hiscore,但它已经被释放,所以你崩溃了。

    没有代码初始化_highscore,它没有崩溃。因为_highscore 总是nil,在Objective-C 中向nil 发送消息是完全可以的;没发生什么事。但是,如果您确实对其进行了初始化,并且仅将其设置为assign,那么它就不再是nil。一旦 ARC 释放它,_highscore 将指向一个不再存在的对象,这就是导致崩溃的原因。

    【讨论】:

    • 好的。是的,我之前曾尝试初始化数组,但后来我的应用程序就会崩溃。完全没有strong 引用导致了这个问题。非常感谢。我一直试图围绕这些概念展开思考。在我接受答案之前的一个后续问题:为什么没有强有力的参考会在这里有所作为?
    • cmets 中没有足够的空间来查看我想说的内容,请参阅上面的编辑。
    • 老兄!现在这完全有道理。感谢您的详尽解释...我不会再犯这个错误了!
    猜你喜欢
    • 2015-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多