Objective-C Reflection для общей реализации NSCoding - PullRequest
12 голосов
/ 28 мая 2009

Существуют ли какие-либо средства отражения в Objective-C, которые позволили бы вам написать общие реализации NSCoding путем проверки открытых свойств объекта и создания общих реализаций encodeWithCoder: и initWithCoder:.

Я имею в виду что-то вроде XStream для Java, которое позволяет использовать универсальный способ сериализации и десериализации объектов Java с использованием отражения. Еще лучше, вероятно, было бы какое-то средство помечать свойства как вещи, которые вы хотите сериализовать или которые являются временными (например, ключевое слово transient в Java).

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

Ответы [ 4 ]

12 голосов
/ 28 мая 2009

Мало того, что это возможно, но у меня есть друг, который сделал именно это. (Вы можете увидеть его блог об этом здесь .) Отражение выполняется с помощью функций времени выполнения Objective C, описанных в Справочнике по времени выполнения Objective C 2.0 . Взгляни.

Обратите внимание, однако, что это будет работать только в том случае, если вам нужно общее поведение сохранения всех переменных экземпляра. Возможно, вы не захотите, чтобы NSView сохранял свое суперпредставление; в таких случаях общий случай не будет работать.

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

6 голосов
/ 02 июня 2009

(я являюсь автором поста в блоге, на который ссылается BJ Homer). Есть несколько предостережений при использовании кода:

  1. Целевой класс должен быть KVC-совместимым. Если вы используете свойства Objective-C 2.0 для своих иваров, то все готово. В противном случае вам нужно убедиться, что ваш класс настроен так, чтобы правильно реагировать на valueForKey: и setValue: forKey: методы.
  2. Код использует недокументированную «особенность» среды выполнения для рекурсивного соответствия классов ivar NSCoding. При компиляции почти вся информация о наборе (AFAIK) удаляется из кода, но для сохранения иваров они также должны соответствовать NSCoding. Я обнаружил, что если ivar является объектом, и я вызываю для него ivar_getTypeEncoding (), я получу обратно строку c, которая выглядит примерно так: {#IVAR_CLASS} (Пример: {#NSString}). Что я делаю, так это получаю строку между # и}, создаю объект Class с помощью NSClassFromString и рекурсирую по этому.
  3. Этот код сохранит ВСЕ ивары.
  4. Используйте на свой страх и риск (конечно)

Я написал это главным образом как доказательство концепции, и было много случаев, когда этот код мог бы сбоить. Например, в объекте A есть ivar для B, а в B есть ivar для A, а затем использование кода для сериализации одного или другого вызовет (я полагаю) бесконечный цикл.

Тем не менее, я думаю, что это чертовски круто, что Objective-C даже позволяет вам делать такие вещи, как прежде.

0 голосов
/ 10 октября 2018

Написал это сегодня вечером именно для этой цели; не забывайте об импорте! Это предполагает, что все @properties в вашем классе имеют NSObject-форму, поэтому NSNumber вместо float и т. Д.

#import <objc/runtime.h>

-(id)initWithCoder:(NSCoder *)decoder {
    if (self = [super init]) {
        uint count;
        objc_property_t *properties = class_copyPropertyList(self.class, &count);
        for (int i = 0; i < count ; i++) {
            const char* propertyName = property_getName(properties[i]);
            NSString *key = [NSString stringWithCString:propertyName encoding:NSUTF8StringEncoding];
            NSValue *value = [decoder decodeObjectForKey:key];
            [self setValue:value forKey:key];
        }
        free(properties);
    }
    return self;
}

- (void)encodeWithCoder:(NSCoder *)encoder {
    uint count;
    objc_property_t *properties = class_copyPropertyList(self.class, &count);
    for (int i = 0; i < count ; i++) {
        const char* propertyName = property_getName(properties[i]);
        NSString *key = [NSString stringWithCString:propertyName encoding:NSUTF8StringEncoding];
        NSValue *value = [self valueForKey:key];
        [encoder encodeObject:value forKey:key];
    }
    free(properties);
}
0 голосов
/ 03 июля 2009

Проверить RMModelObject:

http://www.realmacforge.com/svn/trunk/RMModelObject/

Однако обратите внимание, что ObjC Runtime работает в 10.5 и в симуляторе, но не на реальном iPhone. Обнаружил это нелегким путем ... Может быть, OS 3.0 наконец-то его поддерживает, но я не проверял.

...