проблема сериализации iPhone - PullRequest
3 голосов
/ 11 августа 2009

Мне нужно сохранить свой собственный созданный класс в файл, я обнаружил в Интернете, что хорошим подходом является использование NSKeyedArchiver и NSKeyedUnarchiver Мое определение класса выглядит так:

@interface Game : NSObject <NSCoding> {

    NSMutableString *strCompleteWord;
    NSMutableString *strWordToGuess;

    NSMutableArray *arGuessedLetters;    //This array stores characters
    NSMutableArray *arGuessedLettersPos;  //This array stores CGRects

    NSInteger iScore;
    NSInteger iLives;
    NSInteger iRocksFallen;

    BOOL bGameCompleted;
    BOOL bGameOver;
}

Я реализовал методы initWithCoder: и encodeWithCoder: следующим образом:

- (id)initWithCoder:(NSCoder *)coder
    {   
        if([coder allowsKeyedCoding])
        {
            strCompleteWord = [[coder decodeObjectForKey:@"CompletedWord"] copy];
            strWordToGuess = [[coder decodeObjectForKey:@"WordToGuess"] copy];
            arGuessedLetters = [[coder decodeObjectForKey:@"GuessedLetters"] retain];
        //  arGuessedLettersPos = [[coder decodeObjectForKey:@"GuessedLettersPos"] retain];
            iScore = [coder decodeIntegerForKey:@"Score"];
            iLives = [coder decodeIntegerForKey:@"Lives"];
            iRocksFallen = [coder decodeIntegerForKey:@"RocksFallen"];
            bGameCompleted = [coder decodeBoolForKey:@"GameCompleted"];
            bGameOver = [coder decodeBoolForKey:@"GameOver"];
        }
        else
        {
            strCompleteWord = [[coder decodeObject] retain];
            strWordToGuess = [[coder decodeObject] retain];
            arGuessedLetters = [[coder decodeObject] retain];
        //  arGuessedLettersPos = [[coder decodeObject] retain];
            [coder decodeValueOfObjCType:@encode(NSInteger) at:&iScore];
            [coder decodeValueOfObjCType:@encode(NSInteger) at:&iLives];
            [coder decodeValueOfObjCType:@encode(NSInteger) at:&iRocksFallen];
            [coder decodeValueOfObjCType:@encode(BOOL) at:&bGameCompleted];
            [coder decodeValueOfObjCType:@encode(BOOL) at:&bGameOver];
        }

        return self;
    }

    - (void)encodeWithCoder:(NSCoder *)coder
    {
        if([coder allowsKeyedCoding])
        {
            [coder encodeObject:strCompleteWord forKey:@"CompleteWord"];
            [coder encodeObject:strWordToGuess forKey:@"WordToGuess"];
            [coder encodeObject:arGuessedLetters forKey:@"GuessedLetters"];
            //[coder encodeObject:arGuessedLettersPos forKey:@"GuessedLettersPos"];
            [coder encodeInteger:iScore forKey:@"Score"];
            [coder encodeInteger:iLives forKey:@"Lives"];
            [coder encodeInteger:iRocksFallen forKey:@"RocksFallen"];
            [coder encodeBool:bGameCompleted forKey:@"GameCompleted"];
            [coder encodeBool:bGameOver forKey:@"GameOver"];
        }
        else
        {
            [coder encodeObject:strCompleteWord];
            [coder encodeObject:strWordToGuess];
            [coder encodeObject:arGuessedLetters];
            //[coder encodeObject:arGuessedLettersPos];
            [coder encodeValueOfObjCType:@encode(NSInteger) at:&iScore];
            [coder encodeValueOfObjCType:@encode(NSInteger) at:&iLives];
            [coder encodeValueOfObjCType:@encode(NSInteger) at:&iRocksFallen];
            [coder encodeValueOfObjCType:@encode(BOOL) at:&bGameCompleted];
            [coder encodeValueOfObjCType:@encode(BOOL) at:&bGameOver];
        }
    }

И я использую эти методы для архивирования и разархивирования данных:

[NSKeyedArchiver archiveRootObject:currentGame toFile:strPath];
Game *currentGame = [NSKeyedUnarchiver unarchiveObjectWithFile:strPath];

У меня две проблемы.

1) Как видите, строки с arGuessedLettersPos комментируются, потому что каждый раз, когда я пытаюсь кодировать этот массив, возникает ошибка (этот архиватор не может кодировать структуры), и этот массив используется для хранения структур CGRect. Я видел решение в интернете. Дело в том, что каждый CGRect в массиве преобразуется в NSString (используя NSStringFromCGRect()) и затем сохраняется. Это хороший подход?

2) Это большая проблема для меня. Даже если я прокомментирую эту строку и затем успешно выполню код, затем сохраню (заархивирую) данные и затем попытаюсь загрузить (разархивировать) их, данные не загружаются. Там нет никаких ошибок, но currentGame объект не имеет данных, которые должны быть загружены.

Не могли бы вы дать мне совет? Я впервые использую архиваторы и разархиваторы.

Большое спасибо за каждый ответ.

Ответы [ 4 ]

2 голосов
/ 12 августа 2009

Проблема с загрузкой и сохранением решена другим способом ...

Вместо реализации - (id) initWithCoder: (NSCoder ) кодер и - (void) encodeWithCoder: (NSCoder ) кодер Я использовал это решение:

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

    [archiver encodeObject:self.strCompleteWord forKey:@"CompleteWord"];
    [archiver encodeObject:self.strWordToGuess forKey:@"WordToGuess"];
    [archiver encodeObject:self.arGuessedLetters forKey:@"GuessedLetters"];
    //[coder encodeObject:self.arGuessedLettersPos forKey:@"GuessedLettersPos"];
    [archiver encodeInteger:self.iScore forKey:@"Score"];
    [archiver encodeInteger:self.iLives forKey:@"Lives"];
    [archiver encodeInteger:self.iRocksFallen forKey:@"RocksFallen"];
    [archiver encodeBool:self.bGameCompleted forKey:@"GameCompleted"];
    [archiver encodeBool:self.bGameOver forKey:@"GameOver"];

    [archiver finishEncoding];

    [data writeToFile:strPath atomically:YES];

    [data release];

и

    NSMutableData *data = [[NSMutableData alloc] initWithContentsOfFile:strPath];
    NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];

    self.strCompleteWord = [[unarchiver decodeObjectForKey:@"CompletedWord"] copy];
    self.strWordToGuess = [[unarchiver decodeObjectForKey:@"WordToGuess"] copy];
    self.arGuessedLetters = [[unarchiver decodeObjectForKey:@"GuessedLetters"] retain];
    //self.arGuessedLettersPos = [[unarchiver decodeObjectForKey:@"GuessedLettersPos"] retain];     
    self.iScore = [unarchiver decodeIntegerForKey:@"Score"];
    self.iLives = [unarchiver decodeIntegerForKey:@"Lives"];
 self.iRocksFallen = [unarchiver decodeIntegerForKey:@"RocksFallen"];
   self.bGameCompleted = [unarchiver decodeBoolForKey:@"GameCompleted"];
    self.bGameOver = [unarchiver decodeBoolForKey:@"GameOver"];

    [unarchiver finishDecoding];
    [data release];

И это прекрасно работает:)

1 голос
/ 12 августа 2009

Возможно, мне не хватает этого, но я не вижу очевидных ошибок в этом коде.

Вот несколько идей, которые могут помочь:

  • Добавьте несколько операторов NSLog и просмотрите выходные данные отладки (открываются с помощью команды shift-R в xcode), чтобы увидеть, действительно ли вызываются ваши методы кодирования / декодирования.
  • Убедитесь, что файл архива сохранен : Запустив в симуляторе, вы можете сохранить его на любой локальный путь, например, / tmp / my_archive_file. Попробуйте сохранить в этот файл и посмотрите, существует ли (a) файл с правильной отметкой времени, и (b) вы распечатываете файл, вы можете увидеть некоторые распознаваемые строки (например, «RocksFallen») среди двоичного gooblygoo.

Я также не думаю, что нужно проверять allowsKeyed(En)coding, поскольку вы знаете, что это всегда будет правдой, когда вы явно используете NSKeyed(Un)archiver, чтобы сделать свою грязную работу за вас. Таким образом, вы можете выбросить вторую половину своего кода.

О кодировании этих массивов CGRect s: я не думаю, что вы можете напрямую добавлять структуры в NSMutableArray, верно? Таким образом, они должны быть каким-то объектом, что означает, что вы можете добавить поддержку NSCoding к этому объекту. Если это не ваш собственный объект, вы можете создать его подкласс и добавить этот протокол или попробовать добавить его в категорию.

0 голосов
/ 08 апреля 2010

Лучший способ архивировать структуры (не содержащие указателей) - заключить значения в блок NSData.

здесь, я выгружаю массив массива в архиватор:

for (int i = 0; i < items; i++) {

   NSString *keyValue = [NSString stringWithFormat:@"YourStruct%i", i ];
   NSData    *dataset = [NSData dataWithBytes:(void*)&activeprofile[i] length:(sizeof(profileset))];  

   [encoder encodeObject:dataset forKey:keyValue];    // makes a nice, solid data block

}

и чтение данных обратно в:

for (int i = 0; i < items; i++) {

    NSString *keyValue = [NSString stringWithFormat:@"YourStruct%i", i ];
    NSData    *dataset = [decoder decodeObjectForKey:keyValue];

    [dataset getBytes:&activeprofile[i] length:sizeof(profileset)];

}

Вот и все: просто как пирог и очень гибкий с типами данных!

Аналогичный метод архивации без ключей можно найти здесь: http://borkware.com/quickies/one?topic=NSCoder Хотя я предлагаю придерживаться архивирования с ключами, поскольку NSArchive не поддерживается на iPhone и классифицирован как устаревший код.

0 голосов
/ 12 августа 2009

Спасибо за ответ, Тайлер.

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

Я пытался вставить в код некоторые NSLogs, но не все кажется хорошим Так... Когда я запускаю симулятор в первый раз, нет файла архива, да, это очевидно, потому что ничего не сохраняется. Затем я что-то меняю и выхожу из приложения. Когда приложение закрывается, данные могут быть заархивированы, все в порядке. Заявления NSLog записываются в консоли, файл находится на диске. Но странно то, что когда я запускаю приложение, метод декодирования (initwithcoder) не вызывается .. Не знаю почему. Затем я выхожу из симулятора и снова запускаю симулятор. Когда я снова запускаю его, во время запуска вызывается метод декодирования. Но только при первом запуске после запуска симулятора, когда я затем работаю с симулятором, например, выход из приложения и запускаю его снова, метод initWithCoder не вызывается, и это очень странно для меня ..

Так что есть проблемы с разархивированием, я думаю.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...