iPhone - архивирование массива пользовательских объектов - PullRequest
3 голосов
/ 12 мая 2010

Я пытался часами и не могу решить эту проблему.

Я создаю приложение, которое сохраняет незаконченные шахматные игры, поэтому я пытаюсь записать массив в файл.

Это массив, если он имеет смысл:

-NSMutableArray savedGames
--GameSave a
---NSMutableArray board;
----Piece a, b, c, etc.
-----some ints
---NSString whitePlayer, blackPlayer;
---int playerOnTop, turn;
--GameSave b
---NSMutableArray board;
----Piece a, b, c, etc.
-----some ints
---NSString whitePlayer, blackPlayer;
---int playerOnTop, turn;

и т.д.

А вот мои методы NSCoding:

GameSave.m

- (void)encodeWithCoder:(NSCoder *)coder {
    [coder encodeObject:whitePlayer forKey:@"whitePlayer"];
    [coder encodeObject:blackPlayer forKey:@"blackPlayer"];
    [coder encodeInt:playerOnTop forKey:@"playerOnTop"];
    [coder encodeInt:turn forKey:@"turn"];
    [coder encodeObject:board forKey:@"board"];
}

- (id)initWithCoder:(NSCoder *)coder {
    self = [[GameSave alloc] init];
    if (self != nil)
    {
        board = [coder decodeObjectForKey:@"board"];
        whitePlayer = [coder decodeObjectForKey:@"whitePlayer"];
        blackPlayer = [coder decodeObjectForKey:@"blackPlayer"];
        playerOnTop = [coder decodeIntForKey:@"playerOnTop"];
        turn = [coder decodeIntForKey:@"turn"];
    }   
    return self;
}

Piece.m

- (void)encodeWithCoder:(NSCoder *)coder {
    [coder encodeInt:color forKey:@"color"];
    [coder encodeInt:piece forKey:@"piece"];
    [coder encodeInt:row forKey:@"row"];
    [coder encodeInt:column forKey:@"column"];
}

- (id)initWithCoder:(NSCoder *)coder {
    self = [[Piece alloc] init];
    if (self != nil)
    {
        color = [coder decodeIntForKey:@"color"];
        piece = [coder decodeIntForKey:@"piece"];
        row = [coder decodeIntForKey:@"row"];
        column = [coder decodeIntForKey:@"column"];
    }   
    return self;
}

И этот код пытается архивировать и сохранить в файл:

- (void)saveGame {
    ChessSaverAppDelegate *delegate = (ChessSaverAppDelegate *) [[UIApplication sharedApplication] delegate];
    [[delegate gameSave] setBoard:board];

    NSMutableArray *savedGames = [NSKeyedUnarchiver unarchiveObjectWithFile:[self dataFilePath]];
    if (savedGames == nil) {
        [NSKeyedArchiver archiveRootObject:[delegate gameSave] toFile:[self dataFilePath]];
    } else {
        [savedGames addObject:[delegate gameSave]];
        [NSKeyedArchiver archiveRootObject:savedGames toFile:[self dataFilePath]];
    }
}

- (NSString *)dataFilePath {
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    return [documentsDirectory stringByAppendingPathComponent:@"gameSaves.plist"];
}

Извините, вот проблема:

После установки некоторых точек останова после этой строки из -saveGame возникает ошибка:
[NSKeyedArchiver archiveRootObject: saveGames toFile: [self dataFilePath]];

И это то, что отображается в консоли:

2010-05-11 17:04:08.852 ChessSaver[62065:207] *** -[NSCFType encodeWithCoder:]: unrecognized selector sent to instance 0x3d3cd30
2010-05-11 17:04:08.891 ChessSaver[62065:207] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSCFType encodeWithCoder:]: unrecognized selector sent to instance 0x3d3cd30'
2010-05-11 17:04:08.908 ChessSaver[62065:207] Stack: (
    32339035,
    31077641,
    32720955,
    32290422,
    32143042,
    238843,
    25827,
    238843,
    564412,
    342037,
    238843,
    606848,
    17686,
    2733061,
    4646817,
    2733061,
    3140430,
    3149167,
    3144379,
    2837983,
    2746312,
    2773089,
    41684313,
    32123776,
    32119880,
    41678357,
    41678554,
    2777007,
    9884,
    9738
)

Если это имеет значение, -saveGame вызывается из UIBarButton в контроллере навигации.

Любая помощь приветствуется, спасибо.

Ответы [ 2 ]

7 голосов
/ 12 мая 2010

Это неправильно:

- (id)initWithCoder:(NSCoder *)coder {
    self = [[Piece alloc] init];
    if (self != nil)
    {

Ко времени вызова initWithCoder: экземпляр класса уже распределен. В вашем коде вы пропускаете этот экземпляр. Вы должны делать это:

- (id)initWithCoder:(NSCoder *)coder {
    self = [super init]; // or [self init] if needed
    if (self != nil)
    {

Кроме того, когда вы декодируете объект, вы им не владеете, поэтому вам необходимо сохранить его.

board = [[coder decodeObjectForKey:@"board"] retain];

Если вы не «получаете» правила сохранения / освобождения, изучите их сейчас, или вы будете постоянно стрелять себе в ногу.

Кроме того, у вашего saveGame метода есть несколько проблем.

  1. Если savedGames равен нулю, вы сохраняете в файл объект, а не массив. Это означает, что при следующем запуске неархивированный объект не будет массивом, как вы ожидаете.
  2. Неархивированные массивы неизменны. Объявление типа NSMutableArray не имеет значения, если вы заархивируете изменяемый массив, вы получите неизменный при разархивировании. Вам нужно вызвать mutableCopy для неархивированного массива, если вы хотите изменить его.

Я точно не уверен насчет исключения, но исправьте вышеуказанные проблемы, и либо оно исчезнет, ​​либо решение станет легче найти.

0 голосов
/ 12 мая 2010

Если вы не можете заставить работать ваш NSKeyedArchiver, возможно, другой вариант будет writeToFile:

- (NSString *) saveFilePath:(NSString*)fileName {
 NSArray *saveFilePathArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
 NSString *filePath = [[NSString alloc] initWithFormat:@"%@.extension", fileName];
 return [[saveFilePathArray objectAtIndex:0] stringByAppendingPathComponent:filePath];
 [saveFilePathArray release];
 [filePath release];
}

 ...
 NSMutableArray *yourUnsavedGameObject = [[NSMutableArray alloc] initWithArray:yourUnsavedGame];
 // Write the dictionary to a file
 [yourUnsavedGameObject writeToFile:[self saveFilePath] atomically:YES];
 ...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...