Вопрос по objective-c, cocoa – NSKeyedArchiver и NSKeyedUnarchiver с NSMutableArray

6

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

ФОН:

Я создал небольшую базу данных, которая, по сути, представляет собой NSMutableArray, содержащий пользовательские объекты, которые мы можем вызвать recordObjects. Я установил массив:

database = [[NSMutableArray alloc] init];

и мой пользовательский объект с именем & quot; recordObject & quot; содержит следующие переменные и единицы:

NSString *name;
int       anInt;
Bool      aBool;

Я также синтезировал методы, чтобы я мог делать такие вызовы, как:

aString = [[database objectAtIndex:someIndex] name];

И добавил методы в мой класс контроллера для добавления, удаления и выбора отдельных записей для отображения. Пока все работает правильно и именно так, как и ожидалось.

Затем я настроил свой класс recordObject (подкласс NSObject) для использования NSCoder (включив в него директиву @interface, и добавил следующие пользовательские методы кодировщика и декодера в файл реализации:

-(void) encodeWithCoder: (NSCoder *) encoder {
    [encoder encodeObject: name  forKey: @"recordName"];
    [encoder encodeInt:    anInt forKey: @"recordInteger"];
    [encoder encodeBool:   aBool forKey: @"recordBool"];
}

-(id) initWithCoder: (NSCoder *) decoder {
    name    = [decoder decodeObjectForKey: @"recordName"];
    anInt   = [decoder decodeIntForKey:    @"recordInteger"];
    aBool   = [decoder decodeBoolForKey:   @"recordBool"];
}

Для записи файла я использовал следующее:

[NSKeyedArchiver archiveRootObject:database toFile:myPath];

Когда я запускаю программу, кажется, что все работает правильно. Метод кодировщика вызывается для каждой записи в массиве, а файл записывается на диск. Открытие файла с помощью TextEdit показывает, что данные есть (хотя в большинстве случаев они мне непонятны).

ЭТА ПРОБЛЕМА:

Здесь я сталкиваюсь с препятствием.

Я добавил следующий код, чтобы ЗАГРУЗИТЬ файл в массив моей базы данных:

database = [NSKeyedUnarchiver unarchiveObjectWithFile:myPath];

Когда я снова запускаю программу, на этот раз загружая базу данных, она, кажется, работает правильно. Моим первым тестом было использование метода подсчета NSMutableArray:

x = [database count];

В результате X заполняется правильным количеством записей в файле. Если при сохранении базы данных было 5 записей, для X устанавливается значение 5 после загрузки базы данных при следующем запуске программы.

Теперь вот большая проблема:

Программа вылетает, если я пытаюсь использовать ЛЮБОЙ из моих методов доступа. Например, если я попытаюсь использовать следующее после загрузки базы данных:

aString = [[database objectAtIndex:someIndex] name];

программа вылетает и возвращает в консоли следующую ошибку:

Program received signal:  “EXC_BAD_ACCESS”.
sharedlibrary apply-load-rules all

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

В качестве дополнительного примечания все, что я реализовал, пришло из книги Стивена Кочана «Программирование в Objective-C».

Благодарим за любую идею!

Ваш Ответ

4   ответа
14

Есть несколько проблем с вашим кодом.

ВашinitWithCoder: метод реализован не полностью. Вы должны позвонить[super init] и вернутьсяself, Вы также должныcopy или жеretain строковый объект, иначе он будет автоматически освобожден:

- (id)initWithCoder:(NSCoder *)decoder 
{
    self = [super init];
    if(self)
    {
         name    = [[decoder decodeObjectForKey: @"recordName"] copy];
         anInt   = [decoder decodeIntForKey:    @"recordInteger"];
         aBool   = [decoder decodeBoolForKey:   @"recordBool"];
    }
    return self;
}

Другая проблема с этой строкой:

database = [NSKeyedUnarchiver unarchiveObjectWithFile:myPath];

Это хорошо, за исключением двух вещей:

  1. You're not holding a reference to the object, so it will be autoreleased.
  2. NSKeyedArchiver returns an immutable object, in this case an NSArray and not an NSMutableArray.

Вам нужно сделать это:

database = [[NSKeyedUnarchiver unarchiveObjectWithFile:myPath] mutableCopy];

Это сохранит объект (потому что он является копией) и сделает объектNSMutableArray.

Как и в предыдущем комментарии, разархивирование изменяемого объекта создает изменяемый объект, я попробовал это. Возможно, это была ошибка в предыдущем SDK, которая была исправлена.
Нет, если ты позвонишьcopy наNSMutableArray тогда вы получите неизменныйNSArray, К сожалению, здесь документы рассказывают только половину истории. То же самое происходит сNSCoder, Если вы используетеNSCoder кодироватьNSMutableString затем, когда вы расшифруете его, вы получитеNSString, Вы должны использоватьmutableCopy чтобы получить изменяемые версии обратно.
Ваше мнение об архивации изменяемых объектов неверно. Архивирование и разархивирование изменяемого объекта, такого какNSMutableArray илиNSMutableString не делает его неизменным.
Не будет ли достаточной копии? Согласно документам, unarchiveObjectWithFile: & quot; возвращает граф объектов, ранее закодированный NSKeyedArchivier & quot; В этом случае это был NSMutableArray.
0

У меня возникла та же проблема при разархивировании пользовательского объекта

self.calTable = [[NSKeyedUnarchiver unarchiveObjectWithFile:calibrationFile] objectForKey:@"calTable"];

На основании ответа Роба я изменился на

self.calTable = [[[NSKeyedUnarchiver unarchiveObjectWithFile:calibrationFile] mutableCopy] objectForKey:@"calTable"];

и это исправило все ошибки.

2

Похоже, проблема управления памятью для меня.EXC_BAD_ACCESS обычно означает, что вы пытаетесь получить доступ к объекту, который был освобожден.unarchiveObjectWithFile: возвращает автоматически выпущенный объект, поэтому вы должны сохранить его, если хотите сохранить его, либо с явнымretain или путем присвоения ему оставшейся собственности.

Это проблема памяти, потому что вы обмениваетесь объектами сообщений должным образом не инициализированными.
Я должен упомянуть, что я использую Xcode 3.2.6 на OSX 10.6.8 Greg Steiner
Основываясь на приведенном выше коде, вы видите, или вы могли бы порекомендовать способ сохранить массив базы данных? Сегодня вечером я снова перечитал главу об управлении памятью и посмотрю, смогу ли я это выяснить, но если есть простое исправление, это было бы очень признательно! :) Greg Steiner
Гектометр Это объяснило бы это (я КРАЙНЕ плохо с управлением памятью на данный момент ... все еще пытаюсь обдумать это). Greg Steiner
[database release]; database = [[NSKeyedUnarchiver unarchiveObjectWithFile:myPath] retain];
4

Похоже, что вы инициализируете recordObjects в -initWithCoder:

Попробуйте что-то вроде этого:

-(id) initWithCoder: (NSCoder *) decoder {

    self = [super init];

    if (self){

       name    = [decoder decodeObjectForKey: @"recordName"] copy];
       anInt   = [decoder decodeIntForKey:    @"recordInteger"];
       aBool   = [decoder decodeBoolForKey:   @"recordBool"];
     }

    return self;
}

Данные находятся там, когда вы их архивируете, но вы не разархивируете их должным образом.

Получил это работает. Определенно групповое усилие ... lol Пришлось также СОХРАНИТЬ переменные NSSTring. Спасибо за помощь! Greg Steiner
Я сделаю попытку и доложу. Благодарю. Greg Steiner
Не хорошо. Я даже пытался использовать [[super alloc] init]; но все равно выдает ту же ошибку. Greg Steiner
Опубликуйте весь код архивации / разархивирования, а также код вашей строки myPath
Важной частью является то, что это предложение заканчивается «вернуть себя». Твоему не хватает этого. Вызов super init тоже требуется.

Похожие вопросы