3

我正在编写一个基本游戏,我正在使用一个GameStateManager单例并处理所有状态管理,例如保存、加载、删除方法。

我有一个单独的 singelton 来处理所有的Game东西。游戏对象位于游戏状态管理器中,因此我可以使用 NSCoding 和 Archiving 将其保存。

当我保存状态时,似乎没有问题,并且游戏对象(单例)已正确保存。

但是,当我尝试加载状态(通过重新启动应用程序)时,游戏对象始终为空。

Strangley,如果我删除单例属性并使其成为标准类,这个问题就会消失,我可以加载该类及其所有属性而不会出现任何重大问题。

总之,我这样做:

  1. GameStateManager = Singleton,处理所有游戏状态管理(加载、保存)并拥有一个游戏对象(game)

  2. Game = Singleton,它处理游戏中的事情并采用 NSCoding 协议。

  3. 用游戏对象保存游戏状态很好,对象显然在那里。

  4. 加载游戏状态似乎使游戏对象为空。它应该在那里,但由于某种原因它永远不会加载它。

  5. 如果我删除使游戏类成为单例的所有属性并使其成为普通类,问题似乎就消失了。

我认为这与Game从未初始化的事实有关,但这没有意义,因为我可以让 Game 在它没有单例属性时加载。

代码如下。

// GameStateManager.m

-(void)saveGameState
{
    CCLOG(@"METHOD: saveGameState()");

    self.lastSaveDate   = [NSDate date];

    NSMutableData *data;
    NSString *archivePath = [NSTemporaryDirectory() stringByAppendingPathComponent:kGameSaveFile];
    NSKeyedArchiver *archiver;
    BOOL result;

    data = [NSMutableData data];
    archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];

    [archiver encodeObject:self.lastSaveDate forKey:@"lastSaveDate"];
    [archiver encodeObject:self.game forKey:@"game"];
    [archiver finishEncoding];
    result = [data writeToFile:archivePath atomically:YES];
    [archiver release];

}

-(void)loadGameState
{
    CCLOG(@"METHOD: loadGameState()");


    NSData *data;
    NSKeyedUnarchiver *unarchiver;
    NSString *archivePath = [NSTemporaryDirectory() stringByAppendingPathComponent:kGameSaveFile];

    data = [NSData dataWithContentsOfFile:archivePath];
    unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];

    // Customize unarchiver here
    self.game = [unarchiver decodeObjectForKey:@"game"];
    self.lastSaveDate = [unarchiver decodeObjectForKey:@"lastSaveDate"];

    [unarchiver finishDecoding];
    [unarchiver release];


    CCLOG(@"Last Save Date = %@", self.lastSaveDate);
    NSLog(@"game = %@", self.game);
}

// END OF GAMESTATEMANAGER

// -------------------------

 // Game.h
    @interface Game : NSObject
<NSCoding>
{
    NSMutableArray *listOfCities;
    NSMutableArray *listOfColors;
    NSMutableArray *listOfPlayers;
}

@property (nonatomic, retain) NSMutableArray *listOfCities;
@property (nonatomic, retain) NSMutableArray *listOfColors;
@property (nonatomic, retain) NSMutableArray *listOfPlayers;

+(Game *) sharedGame;


 //
// Game.m
// This is a cut-down version of the game object
// Game is a singelton
// The listOfCities, etc are arrays
//
@implementation Game
SYNTHESIZE_SINGLETON_FOR_CLASS(Game)
@synthesize listOfCities, listOfPlayers;

#pragma mark - NSCoding


-(id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super init];

    if (self) 
    {
        self.listOfCities = [aDecoder decodeObjectForKey:@"listOfCities"];
        self.listOfPlayers = [aDecoder decodeObjectForKey:@"listOfPlayers"];

        NSLog(@"Cities = %d", [self.listOfCities count]);
        NSLog(@"Players = %d", [self.listOfPlayers count]);
    }

    return self;
}

-(void)encodeWithCoder:(NSCoder *)aCoder
{
    // Archive objects
    NSLog(@"Cities = %d", [self.listOfCities count]);
    NSLog(@"Players = %d", [self.listOfPlayers count]);

    [aCoder encodeObject:self.listOfCities forKey:@"listOfCities"];
    [aCoder encodeObject:self.listOfPlayers forKey:@"listOfPlayers"];
}

@end

我的问题是,如何使用 NSCoding 和 Archiver 成功保存和加载 Objective C 单格?

编辑;

我努力了:

// In the GameStateManager
#pragma mark - NSCoding

-(id)initWithCoder:(NSCoder *)decoder {
    if (self = [super init]) {
        self.lastSaveDate = [[decoder decodeObjectForKey:@"lastSaveDate"] retain];
        self.game = [[decoder decodeObjectForKey:@"game"] retain];
    }
    return self;
}
-(void)encodeWithCoder:(NSCoder *)encoder {
    [encoder encodeObject:self.lastSaveDate forKey:@"lastSaveDate"];
    [encoder encodeObject:self.game forKey:@"game"];
}

// In Game.m
    self.listOfCities = [[aDecoder decodeObjectForKey:@"listOfCities"] retain];
        self.listOfPlayers = [[aDecoder decodeObjectForKey:@"listOfPlayers"] retain];

        NSLog(@"Cities = %d", [self.listOfCities count]);
        NSLog(@"Players = %d", [self.listOfPlayers count]);
4

4 回答 4

6

Since you're dealing with a singleton, you only ever want a single instance of the class to exist at any time. So you will want to archive and unarchive that single instance only.

Consider this code (assumes ARC is enabled):

@interface Singleton : NSObject <NSCoding>

+ (id)sharedInstance;
@property (strong, nonatomic) NSString *someString;

@end

@implementation Singleton

+ (id)sharedInstance {
    static Singleton instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[Singleton alloc] init];
    });
    return instance;
}

#pragma mark - NSCoding implementation for singleton

- (id)initWithCoder:(NSCoder *)aDecoder {
    // Unarchive the singleton instance.
    Singleton *instance = [Singleton sharedInstance];

    [instance setSomeString:[aDecoder decodeObjectForKey:@"someStringKey"]];

    return instance;
}

- (void)encodeWithCoder:(NSCoder *)aCoder {
    // Archive the singleton instance.
    Singleton *instance = [Singleton sharedInstance];

    [aCoder encodeObject:[instance someString] forKey:@"someStringKey"]];
}

@end

Using this template, you can archive/unarchive the singleton, like this:

// Archiving calls encodeWithCoder: on the singleton instance.
[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:[Singleton sharedInstance]] forKey:@"key"];

// Unarchiving calls initWithCoder: on the singleton instance.
[[NSKeyedUnarchiver unarchiveObjectWithData:[[NSUserDefaults standardUserDefaults] objectForKey:@"key"]];
于 2012-10-10T17:41:52.670 回答
1

在你的 initWithCoder 方法中试试这个:

self.listOfCities = [[aDecoder decodeObjectForKey:@"listOfCities"] retain];
self.listOfPlayers = [[aDecoder decodeObjectForKey:@"listOfPlayers"] retain];
于 2012-07-14T13:36:22.787 回答
1

最简单的方法:

1.加载单例:

+ (AppState *)sharedInstance
{
    static AppState *state = nil;
    if ( !state )
    {
       // load NSData representation of your singleton here.
        NSData *data =[[NSUserDefaults standardUserDefaults] objectForKey:@"appStateData"];

        if ( data )
        {
             state = [NSKeyedUnarchiver unarchiveObjectWithData:data];
        }
        else
        {
            state = [[AppState alloc] init];
        }
    }
    return state;
}

2. 将单例保存在磁盘上

- (BOOL)save
{
    NSData *appStateData = [NSKeyedArchiver archivedDataWithRootObject:self];
    // now save NSData to disc or NSUserDefaults.
    [[NSUserDefaults standardUserDefaults] setObject:appStateData forKey:@"appStateData"];
}

3、实施NSCoding方法:

- (id)initWithCoder:(NSCoder *)coder;
- (void)encodeWithCoder:(NSCoder *)coder;

完毕!

于 2014-04-25T13:30:15.443 回答
0

我正在使用一种混合方法,其中包含Eric Bakerskywinder的答案。

+ (GameStateManager *)sharedInstance {
    static GameStateManager *instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:@"key"];
        if (data) {
            instance = [NSKeyedUnarchiver unarchiveObjectWithData:data];
        } else {
            instance = [[GameStateManager alloc] init];
        }
    });
    return instance;
}
于 2017-08-02T21:06:10.357 回答