NSCoder - архивировать указатель? - PullRequest
3 голосов
/ 18 мая 2009

У меня есть классjective-c, который содержит указатель на другой класс. Я хочу заархивировать экземпляр этого класса через NSCoder:

@interface Barn
{
    int m_numHorses;

    // Barn does not allocate this instance, it just points to it.
    Farmer* m_pFarmer;
}
@end

...

- (void)encodeWithCoder:(NSCoder *)encoder
{
    [encoder encodeInt:m_numHorses forKey:@"numHorses"];
    [encoder encode?:m_pFarmer forKey:@"pFarmer"];
}

- (void) setPointer:(Farmer*)pFarmer
{
    m_pFarmer = pFarmer;
}

Как мне заархивировать указатель m_pFarmer? Это не имеет смысла для меня, так как все, что это - адрес, и я не вижу, что NSCoder мог бы сериализовать на диск для вас, чтобы он знал, как восстановить ссылку позже, когда вы десериализовали?

Ответы [ 5 ]

7 голосов
/ 18 мая 2009

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

encodeValueOfObjCType:@encode(id) at:&m_pFarmer 

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

Если Barn не владеет Farmer, то Barn не должен воссоздавать его при десериализации; в итоге вы получите новый Farmer, который не отделен от исходного. Таким образом, вам нужен способ найти десериализованный экземпляр оригинального Фермера и заменить экземпляр Фермера Барна другим.

Кто-то владеет Farmer, верно? Поэтому Barn должен иметь метод findMyFarmer, который просматривает все FarmerOwner s и находит исходный экземпляр, который он должен использовать. (Может быть, сравнивая farmerID ivar на Farmer?) Как только это будет сделано, вы можете внедрить -[NSObject awakeAfterUsingCoder:(NSCoder *)] на Barn для запуска процедуры замены фермера.

Надеюсь, это имеет смысл. Посмотрите документацию об архивации и сериализации, особенно страницу Кодирование и декодирование объектов , чтобы узнать больше о замене объектов на лету.

Обновление

NSArchiver s и NSKeyedArchiver s поддерживают идею условного архивирования , (через encodeConditionalObject:), в котором объект добавляется в архив, только если какой-то другой Объект в архиве уже добавлен. Документация гласит: " Как правило, условные объекты используются для кодирования слабых или не сохраненных ссылок на объекты. ". Так что, если ваш Farmer уже архивируется, вам нужно добавить его, но если вы только кодируете свой Barn без какого-либо фермера, то вам не захочется.

Обязательно ознакомьтесь с документацией, указанной выше.

3 голосов
/ 18 мая 2009

Вы должны сначала прочитать документы, на которые указывает BJ.

Библиотеки сериализации достаточно умны, чтобы обнаруживать повторы одного и того же объекта, и кодируют его только один раз. В общем, вам следует кодировать этот объект, если он понадобится вам позже. Библиотеки сериализации могут даже обрабатывать циклы графов. Вам не нужно беспокоиться об этих вещах, и активно следует , а не пытаться угадать, как другие объекты будут обрабатывать этот объект. ObjC не имеет таких строгих концепций владения объектами, которые часто требуются C ++. Управление памятью с подсчетом ссылок не требует этого, и вам не следует создавать его заново.

Говоря о подсчете ссылок, вы не хотите оставить Фермера в своем сеттере? Это потому, что Барн уже задерживает Фермера? Или это код для сбора мусора? Или это было непреднамеренное неполное удержание? Это может определенно вызвать сбой при десериализации этого материала, если вы его недостаточно сохранили.

3 голосов
/ 18 мая 2009

Вы должны использовать encodeObject: и реализовать NSCoding в своем классе Farmer, чтобы он вызывался рекурсивно.

0 голосов
/ 07 февраля 2015

Я сделал что-то подобное. В вашем случае я бы сгенерировал UUID (уникальный идентификатор) для фермера, используя

var uniqueIdentifier: String = NSUUID().UUIDString

Тогда я бы сохранил этот UUID на Фермере, используя NSCoding.

Я бы также указывал пункт Barn на Фермера через этот UUID (вы можете иметь вычисленное свойство «Фермер» в Barn, определяющее Фермера через UUID). И сохраните UUID сарая с NSCoding. Таким образом, вы никогда не потеряете указатель при сохранении данных на диске.

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

РЕДАКТИРОВАТЬ: у меня был случай, в котором я пытался закодировать указатель на другой объект с NSCoding. Моим случаем было оповещение о наличии акций (определяемое как свойство акции). Похоже, что NSCoding создал копию исходного объекта, «замороженного» в его текущем состоянии. И эта копия будет жить независимо от исходного объекта. Акции имеют свойства, некоторые сохраняются (текущая цена акции), другие рассчитываются (прибыль в валюте или в%). Сохраненные свойства свойства акции оповещения о запасах не изменились, а вычисленные свойства (которые зависели от других факторов) эволюционировали. Таким образом, из моего опыта можно сделать вывод, что пытаться сохранить указатель на другой объект с помощью NSCoding не очень хорошая идея.

0 голосов
/ 18 мая 2009

Если вы буквально хотите закодировать адрес, удерживаемый указателем (поскольку вы можете гарантировать, что объект Farmer будет оставаться в этом месте до тех пор, пока вы не расшифруете архив), вы можете закодировать указатель как NSInteger.

т.е. [encoder encodeInteger:(NSInteger)m_pFarmer forKey:@"pFarmer"];

В общем, это плохая идея, если только вы не будете очень осторожно держать объект Farmer дольше, чем архив.

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