Как NSCoder и/или NSKeyedUnarchiver обрабатывают множественные декодирования одного и того же объекта?

Мне было интересно, как NSCoder будет обрабатывать объект, который был разделен и закодирован несколькими объектами при следующем декодировании. Будет ли он создавать две копии объекта или один объект будет декодирован и разделен между всеми другими объектами, которые его декодируют?

Ниже я привел небольшой пример такой ситуации.

Пример:

  1. Приложение запускается
  2. Объект A и объект B устанавливают объект C в качестве своего делегата.
  3. Приложение получает уведомление о завершении.
  4. Объект A и объект B кодируют себя и все свои объекты (включая их делегаты)
  5. Приложение закрывается и перезагружается
  6. Объект A и объект B декодируют себя и все свои объекты (включая их делегаты)

Будут ли Объект А и Объект Б совместно использовать один и тот же декодированный объект после шага 6 или каждый из них будет иметь свою собственную копию?


person Eric L    schedule 26.09.2011    source источник


Ответы (3)


Они будут совместно использовать ссылку на один и тот же объект (если только вы не пойдете на все, чтобы изменить это поведение).

т.е. NSCoding может работать с полностью циклическими, всенаправленными, сложно связанными графами объектов (при условии, что все участники графа правильно поддерживают NSCoding).

Обратите внимание, что кодирование делегатов крайне нетипично. Делегаты обычно подключаются к графу неархивированных объектов после анархирования, и делегаты действуют как своего рода канал между вашим заархивированным слоем модели (или заархивированным слоем представления, в случае IB — история более сложная, действительно, для файлов XIB... но... достаточно близко) и остальной части вашего приложения.

person bbum    schedule 26.09.2011
comment
Да, я бы обычно не кодировал делегата. Я просто не мог придумать лучшего примера в короткие сроки. Я полагаю, что мог бы использовать мою реальную ситуацию, но я печатал больше, чем нужно. В любом случае, спасибо! - person Eric L; 27.09.2011
comment
Не беспокойтесь - просто хотел убедиться. :) - person bbum; 27.09.2011

Это отличный вопрос, и я всегда задавался этим вопросом. Итак, я написал небольшую тестовую программу, чтобы попробовать ее. Четыре класса: ClassA, ClassB, ClassC и MyBaseClass. ClassA, ClassB и ClassC наследуются от MyBaseClass, который соответствует NSCoding и предоставляет два свойства: name и age. Классы A, B и C идентичны, за исключением того, что ClassA и ClassB также содержат ссылку на экземпляр ClassC. ClassA и ClassB также переопределяют initwithCoder: и encodeWithCoder: для декодирования и кодирования их ссылок на экземпляр ClassC. Вот как выглядит код верхнего уровня:

ClassA *a = [[ClassA alloc] initWithName:@"Mr. A" age:11];
ClassB *b = [[ClassB alloc] initWithName:@"Mrs. B" age:22];
ClassC *c = [[ClassC alloc] initWithName:@"Ms. C" age:33];

b.c = c;
a.c = c;

NSArray *rootObject = @[a, b, c];
NSString *const kFilePath = @"/Users/myname/Documents/testarchive";
BOOL result = [NSKeyedArchiver archiveRootObject:rootObject toFile:kFilePath];
NSLog(@"result = %@", (result) ? @"YES" : @"NO");

NSArray *newRootObject = [NSKeyedUnarchiver unarchiveObjectWithFile:kFilePath];
NSLog(@"new root object = %@", newRootObject);

Объекты сериализуются и десериализуются отлично. Кроме того, после десериализации a.c и b.c указывают на один и тот же экземпляр ClassC, то есть их указатели на объекты имеют одинаковый адрес.

По-видимому, внутри encodeObject:forKey: NSKeyedArchiver делается тест, чтобы увидеть, является ли кодируемый объект isEqualTo: ранее закодированным объектом, и если это так, то вместо полного объекта сохраняется ссылка. Обратное должно произойти в decodeObject:forKey: NSKeyedUnarchiver. Очень круто!

person Steve    schedule 11.10.2013

Я не думаю, что первый ответ полностью правильный. Согласно документации Apple, «сериализация сохраняет только значения объектов и их положение в иерархии. Множественные ссылки на один и тот же объект значения могут привести к появлению нескольких объектов при десериализации».

Таким образом, не гарантируется, что один сериализованный объект приведет к одному объекту при десериализации из этих нескольких NSCoders.

Если ваша реализация похожа на ваш пример, возможно, вы не совсем правильно думаете о вещах. Если вы думаете о логической организации приложения, может иметь смысл, что несколько объектов могут совместно использовать один и тот же делегат. Но в целом я бы не ожидал, что кто-то будет использовать протокол NSCoder для кодирования/декодирования делегатов. Обычно я ожидаю, что делегат будет кодировать/декодировать объекты, для которых он является делегатом.

Например, давайте посмотрим на NSTableView. Возможно, пользователь получает возможность настроить способ отображения NSTableView (возможно, пользователю разрешено изменять размер столбцов или выбирать, какие столбцы отображать). Это полезная информация, которую вы можете сохранить и восстановить с помощью протокола NSCoding. У NSTableView также есть делегаты. Делегат должен быть контроллером (из парадигмы MVC), и его никогда не нужно кодировать/декодировать с использованием NSCoding, потому что это общий код, который не должен поддерживать какое-либо состояние во время выполнения.

Поэтому в идеале вы создаете своего делегата/контроллера, используя метод инициализации. Он понимает, что ему нужно настроить NSTableView так, чтобы он выглядел так, как он выглядел в последний раз, когда пользователь настраивал его, поэтому он извлекает старое табличное представление с диска с помощью NSCoding, а затем отображает его для пользователя так же, как это было в последний раз, когда они его видели. .

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

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

person Carter    schedule 27.09.2011
comment
Справедливо, но не отвечает на вопрос о поведении архиватора. Это может быть комментарий к вопросу или краткая заметка, например, например. ответ ббума. - person Ben Zotto; 27.09.2011
comment
Несколько ссылок на один и тот же объект-значение могут привести к созданию нескольких объектов при десериализации. Лучше бы эти объекты были такими же -isEqual: и -hash. Это должно быть уточнено в документах. - person bbum; 27.09.2011
comment
Мой ответ был в первом абзаце. - person Carter; 27.09.2011
comment
Предложение, которое вы цитируете из документации Apple, относится к сериализации, например. NSPropertyListSerialization, не архивировать с NSCoder. Это ясно видно, например. следующим предложением: «Поскольку сериализация не сохраняет информацию о классе или изменчивость, а также не обрабатывает несколько ссылок, кодирование (реализованное NSCoder и его подклассами) является предпочтительным способом сделать графы объектов постоянными». Кроме того, в вопросе Эрика нет упоминания о нескольких NSCoders. - person Stephan Tolksdorf; 06.09.2017