Прямая совместимость и обратная совместимость для включенных архивов

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

Преимущества включенной архивации

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

Для максимальной совместимости необходимо быть в состоянии сделать следующее:

Первые два элемента обеспечивают полную обратную совместимость: старые и текущие версии класса могут считать каждого архивы других. Для достижения этой возможности важно, что Вы знаете, какие значения были закодированы всеми предыдущими версиями Вашего класса, который необходимо поддерживать, а также как предыдущие версии декодируют себя. Если у Вас нет этой информации, можно быть в состоянии вывести некоторые вещи из существующих архивов и существующих реализаций NSCoding методы.

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

Общие советы при поддержании совместимости

Для простой идентификации версии декодируемого класса можно добавить некоторую информацию о версии к архиву. Это может быть любым типом информации, которую Вы хотите, не только целое число (такое как версия класса), как это было с невключенным кодированием. Можно просто закодировать целое число «версии» или строку с некоторым ключом, или в некоторых редких случаях можно хотеть объект словаря, полный положительных героев. Конечно, добавление некоторой информации о версии сегодня предполагает, что у Вас также есть план относительно контакта с различными версиями в Вашем initWithCoder: сегодня. В противном случае изменение информации о версии в будущем не принесет текущей версии пользы класса.

Не забудьте сохранять Ваш NSCoding реализации синхронизировались. Каждый раз, когда Вы изменяетесь, как Вы выписываете состояние объектов в классе encodeWithCoder: метод, необходимо обновить Ваш initWithCoder: метод для понимания новых ключей. Поскольку информация во включенном архиве может кодироваться и декодироваться в любом порядке, двух NSCoding методы не должны обрабатывать ключи в той же последовательности. Использование любые последовательности является самым удобным для каждого метода.

Добавление новых значений к ключам

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

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

Если Вы вносите это изменение, и предыдущая версия не делала скидку на изменение, или допуски недостаточны или недопустимы, вероятно, необходимо создать совершенно новый ключ для нового состояния (см. Добавляющие Новые Ключи), и сделайте старый ключ устаревшим (см. Удаляющие или Уходящие в отставку Ключи).

Добавление новых ключей

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

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

При декодировании более старых архивов Вы должны быть подготовлены обработать отсутствие нового ключа. При необходимости можно все еще попытаться декодировать новый ключ и просто принять значение по умолчанию для недостающего ключа (nil, 0, NSZeroPoint, и т.д.). Значение по умолчанию кодера может не быть допустимым для каждого ключа, как бы то ни было. В этом случае необходимо обнаружить значение по умолчанию и заменить более разумным собственным значением по умолчанию. Если новый ключ является заменой для более старого ключа, надлежащая замена должна прибыть из старого ключа, который может потребовать отображения старого значения к одному из позволенных значений для нового ключа. Если необходимо различить значение по умолчанию для недостающего ключа и то же значение для существующего ключа, используйте NSCoder метод containsValueForKey:.

Если новый ключ заменяет более старый ключ, необходимо должным образом обработать устаревший ключ (см. Удаляющие или Уходящие в отставку Ключи).

Удаление или уходящие в отставку ключи

Поскольку класс развивается, некоторая информация может стать устаревшей или замененной более новой реализацией.

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

При декодировании будущих архивов Вы должны быть подготовлены обработать недостающие ключи. При необходимости можно просто принять значение декодирования по умолчанию для недостающих ключей (nil, 0, NSZeroPoint, и т.д.). Если значение по умолчанию кодера не допустимо для определенного ключа, необходимо обнаружить значение по умолчанию и заменить более разумным собственным значением по умолчанию. Если необходимо различить значение по умолчанию для недостающего ключа и то же значение для существующего ключа, используйте NSCoder метод containsValueForKey:. Таким образом Вы даете себе гибкость, чтобы прекратить кодировать определенные значения позже.

В случаях, где необходимо отказаться от старого ключа для более нового, но старый класс не может обработать недостающий ключ соответственно, необходимо продолжить писать некоторое значение для старого ключа, а также более нового ключа. Значение должно быть чем-то, что старый класс может понять и должен, вероятно, быть максимально близким моделированием нового состояния. Например, рассмотрите класс, первоначально прибывший в «ваниль», «шоколад», и “разновидности” ореха пекан масла и теперь имеющий дополнительный “двойной шоколад” и разновидности «карамели». Для кодирования значения для старого ключа можно отобразить “двойной шоколад” на значение для «шоколада» в старом классе, но Вам, вероятно, придется отобразить «карамель» на «ваниль». Конечно, Вы пишете весь новый набор значений с новым ключом и Вашим initWithCoder: метод должен предпочесть использовать новый ключ при наличии.

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

Рассмотрите как пример класс под названием Изображение, представляющее изображения. (Этот пример не обязательно отражает фактическое поведение любого класса изображения, как NSImage.) Предположим класс Изображения в состоянии закодировать свои экземпляры URL, JPEG или GIF, в зависимости от того, какой бы ни является самым удобным для определенного экземпляра. Закодированный Объект изображения, поэтому, содержит только один из следующих ключей: @"URL", @"JPEG", @"GIF". initWithCoder: метод проверяет на ключи в порядке @"URL", @"JPEG", @"GIF", и инициализирует себя с первым представлением, которое это находит. В будущем могло бы случиться так, что ни один из них не прост или удобен заархивировать (например, беря любые данные, которые действительно имеет экземпляр Изображения, и преобразование его к JPEG могло бы быть довольно дорогим).

Пример нейтрализации, обрабатывающей в этом случае, должен был бы допускать дополнительный ключ (или группа ключей), как @"rawdata", это понимается и используется Изображением initWithCoder: метод, если ни один из других ключей для этого значения (данные изображения) не присутствует. Значение @"rawdata" ключ мог бы быть определен, например, чтобы быть NSData объект, содержащий 32-разрядные пиксели RGBA. Могли бы также быть вторичные ключи как @"pixelshigh" и @"pixelswide" это initWithCoder: искал бы для получения, минимальный набор информации должен был произвести экземпляр Изображения из заархивированной информации. В будущем процесс кодирования для Изображения мог бы выписать удобную информацию, независимо от того, что это в то время и должно было бы также выписать @"rawdata" и другие ключи, чтобы позволить старым декодерам читать объект.

Изменение диаметров долота значений

В некоторых ситуациях у Вас могут быть код и архивы, которые Вы используете на 32-и 64-разрядные платформы (например, у Вас могли бы быть iOS и версии OS X приложения, совместно использующие данные через iCloud). В этих случаях необходимо заботиться, чтобы гарантировать, что целочисленные значения обрабатываются правильно.

Кодирование, что является 32-разрядным целым числом как 64-разрядным целым числом, является не обязательно лучшим решением. Дополнительные старшие нулевые биты, которые Вы даете значению, как Вы даете его archiver, потрачены впустую. С другой стороны, это не вредно также и просто реализовать.

Обобщение encodeInt:forKey: и decodeIntForKey: методы читают и пишут что собственный компонент int размер находится на компьютере. На 64-разрядном компьютере, int может быть 64 бита шириной (или это не могло бы быть; язык C гибок в этом отношении). Поэтому возможно, что значения, требующие, чтобы больше чем 32 бита представляли, могут быть записаны encodeInt:forKey: метод. Если такой архив транспортируется к 32-разрядному компьютеру, decodeIntForKey: метод может быть неспособен представлять то целое число в int возвращаемое значение, и должно бросить NSRangeException.

Полезно ли попытаться обработать это, всегда декодируя такие целые числа, поскольку 64-разрядный спорно. Если целое число является «количеством» чего-то, например, может быть физически невозможно иметь больше, чем 2^32 того, что это находится на 32-разрядном компьютере, поэтому дальнейшая попытка разархивировать файл является, вероятно, пустой тратой времени, и исключение разумно. Также Вы могли бы хотеть или поймать исключение или выполнить Ваши собственные границы, проверяющие 64-разрядное декодируемое значение и возврат nil от initWithCoder:. Однако вызывающая сторона decodeObjectForKey: методу, распаковывающему экземпляр Вашего класса, может не понравиться nil больше, чем исключение, и мог бы закончить тем, что повысил собственное исключение, которое менее понятно относительно причины проблемы, чем исключение диапазона, возможно, было.