Как NSCoder и / или NSKeyedUnarchiver обрабатывают несколько декодирований одного и того же объекта? - PullRequest
3 голосов
/ 27 сентября 2011

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

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

Пример:

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

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

Ответы [ 3 ]

7 голосов
/ 27 сентября 2011

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

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

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

5 голосов
/ 11 октября 2013

Это отличный вопрос, и я всегда задавался вопросом об этом сам. Итак, я написал небольшую тестовую программу, чтобы попробовать. Четыре класса: 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 - то есть их указатели на объекты имеют одинаковый адрес.

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

0 голосов
/ 27 сентября 2011

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

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

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

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

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

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

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

...