Кодирование и декодирование объектов

Для поддержки кодирования и декодирования экземпляров класс должен принять NSCoding протокол и реализация его методы. Этот протокол объявляет два метода, отправляющиеся в закодированные объекты или декодирующиеся.

В соответствии с объектно-ориентированными принципами разработки, объект, закодированный или декодируемый, ответственен за кодирование и декодирование его состояния. Кодер сообщает объекту сделать так путем вызова encodeWithCoder: или initWithCoder:. encodeWithCoder: метод сообщает объекту закодировать его состояние с предоставленным кодером; объект может получить этот метод любое число раз. initWithCoder: сообщение сообщает объекту инициализировать себя от данных в предоставленном кодере; как таковой, это заменяет любой другой метод инициализации и отправляется только один раз на объект.

Кодирование объекта

Когда объект получает encodeWithCoder: если его суперкласс также соответствует, сообщение, это должно закодировать все свое жизненно важное состояние (часто представляемый его свойствами или переменными экземпляра) после передачи сообщения к его суперклассу NSCoding протокол. Объект не должен кодировать все свое состояние. Некоторые значения могут не быть важными для восстановления, и другие могут быть получаемыми от связанного состояния после декодирования. Другое состояние должно быть закодировано только при определенных условиях (например, с encodeConditionalObject:, как описано в Условных Объектах).

Вы используете методы кодирования, такой как encodeObject:forKey:, закодировать ID, скаляры, C массивы, структуры, строки и указатели на любой из этих типов. Посмотрите NSKeyedArchiver спецификация класса для списка методов. Например, предположите создание класса Лица, имеющего свойства для имени, фамилии и высоты; Вы могли бы реализовать encodeWithCoder: следующим образом

- (void)encodeWithCoder:(NSCoder *)coder {
    [coder encodeObject:self.firstName forKey:ASCPersonFirstName];
    [coder encodeObject:self.lastName forKey:ASCPersonLastName];
    [coder encodeFloat:self.height forKey:ASCPersonHeight];
}

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

- (void)encodeWithCoder:(NSCoder *)coder {
    [super encodeWithCoder:coder];
    // Implementation continues.

@encode() директива компилятора генерирует код типа Objective C от выражения типа, которое может использоваться в качестве первого параметра encodeValueOfObjCType:at:. См. “Кодировки Типа” в Языке программирования Objective C для получения дополнительной информации.

Декодирование объекта

В реализации initWithCoder: метод, объект должен сначала вызвать определяемый инициализатор своего суперкласса для инициализации наследованного состояния, и затем это должно декодировать и инициализировать свое состояние. Ключи могут декодироваться в любом порядке. Реализация лица initWithCoder: мог бы быть похожим на это:

- (id)initWithCoder:(NSCoder *)coder {
    self = [super init];
    if (self) {
        _firstName = [coder decodeObjectForKey:ASCPersonFirstName];
        _lastName = [coder decodeObjectForKey:ASCPersonLastName];
        _height = [coder decodeFloatForKey:ASCPersonHeight];
    }
    return self;
}

Если суперкласс принимает NSCoding протокол, Вы запускаете путем присвоения возвращаемого значения initWithCoder: к self:

- (id)initWithCoder:(NSCoder *)coder {
    self = [super initWithCoder:coder];
    if (self) {
        // Implementation continues.

Это сделано в подклассе потому что суперкласс в его реализации initWithCoder:, может решить возвратить объект кроме себя.

Соображения производительности

Чем меньше Вы кодируете для объекта, тем меньше необходимо декодировать, и и запись и чтение архивов становятся быстрее. Прекратите выписывать элементы, которые больше не являются подходящими для класса (но который необходимо было продолжать писать при невключенном кодировании).

Кодирование и декодирование булевских переменных являются быстрее и более дешевыми, чем кодирование и декодирование отдельных 1-разрядных битовых полей как целые числа. Однако кодирование многих битовых полей в единственное целочисленное значение может быть более дешевым, чем кодирование их индивидуально, но это может также усложнить усилия по совместимости позже (см. Структуры и Битовые поля).

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

Это быстрее, чтобы просто декодировать значение для ключа, чем проверить, существует ли это, и затем декодируйте его, если это существует. Вызвать containsValueForKey: только, когда необходимо отличить возвращаемое значение по умолчанию (вследствие несуществования) от действительного значения, которое, оказывается, совпадает со значением по умолчанию.

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

Избегайте использования «$» как префикса для Ваших ключей. Включенный archiver и unarchiver используют ключи, снабженные префиксом «$» для внутренних значений. Несмотря на то, что они тестируют на и искажают определяемые пользователем ключи, имеющие префикс «$», эти издержки делают архивацию медленнее.

Создание замен во время кодирования

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

Методы замены объявляются NSObject, и приезжайте в две разновидности: универсальный и специализированный. Это общие методы:

Метод

Типичное использование

classForCoder

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

replacementObjectForCoder:

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

awakeAfterUsingCoder:

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

Специализированные методы замены походят classForCoder и replacementObjectForCoder:, но они разработаны для (и вызваны), определенный, конкретный подкласс кодера. Например, classForArchiver и replacementObjectForPortCoder: используются NSArchiver и NSPortCoder, соответственно. Путем реализации этих специальных методов класс может базировать свое поведение кодирования на определенном используемом классе кодера. Для получения дополнительной информации об этих методах см. их описания метода в NSObject спецификация класса.

В дополнение к методам, просто обсужденным, NSKeyedArchiver и NSKeyedUnarchiver позвольте объекту делегата выполнить заключительную замену прежде, чем закодировать и после декодирования объектов. Делегат к NSKeyedArchiver объект может реализовать archiver:willEncodeObject: и делегат к NSKeyedUnarchiver объект может реализовать unarchiver:didDecodeObject: выполнять замены.

Ограничение поддержки кодера

В некоторых случаях класс может реализовать NSCoding протокол, но не поддержка один или несколько типов кодера. Например, классы NSDistantObject, NSInvocation, NSPort, и их подклассы принимают NSCoding только для использования NSPortCoder в распределенной системе объектов; они не могут быть закодированы в архив. В этих случаях класс может протестировать, имеет ли кодер определенный тип, и повысьте исключение, если это не поддерживается. Если ограничение должно только ограничить класс последовательных или включенных архивов, можно отправить сообщение allowsKeyedCoding к кодеру; иначе, можно протестировать идентификационные данные класса кодера как показано в следующей выборке.

- (void)encodeWithCoder:(NSCoder *)coder {
    if ([coder isKindOfClass:[NSKeyedArchiver class]]) {
        // encode object
    }
    else {
        [NSException raise:NSInvalidArchiveOperationException
                    format:@"Only supports NSKeyedArchiver coders"];
    }
}

В других ситуациях может наследоваться класс NSCoding от суперкласса, но подкласса может не хотеть поддерживать кодирование. Например, NSWindow наследовался NSCoding от NSResponder, но это не поддерживает кодирование. В этих случаях класс должен переопределить initWithCoder: и encodeWithCoder: методы так, чтобы они повысили исключения, если они когда-либо вызываются.