Кодировать все свойства NSObject в NSData

Итак, я новичок в iOS, но меня немного сбивает с толку сложность простой задачи. Я пытаюсь сохранить свой собственный класс NSObject под названием «Автомобиль» в NSUserDefaults. Очевидно, что это невозможно сделать, поэтому мне нужно сначала закодировать его в NSDATA. Отлично.

Но это означает, что мне нужно также закодировать каждое свойство класса в декодировании...

В моем классе транспортных средств...

- (void) encodeWithCoder: (NSCoder *) coder
{
    [coder encodeInt: x  forKey: @"x"];
    [coder encodeInt: y  forKey: @"y"];
    [coder encodeInt: direction  forKey: @"direction"];

} // encodeWithCoder

- (id) initWithCoder: (NSCoder *) coder
{
    if (self = [super init]) {
        x = [coder decodeIntForKey: @"x"];
        y = [coder decodeIntForKey: @"y"];
        direction = [coder decodeIntForKey: @"direction"];
    }

    return (self);

} // initWithCoder

Если я закончу тем, что добавлю новое свойство в класс транспортного средства, я также должен добавить логику кодирования и декодирования. То же самое для создания копии класса с помощью CopyWithZone. Это оставляет 3 или 4 области, где добавление нового свойства в класс может пойти не так.

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

Итак, я думаю, мой вопрос:

  1. Разве об этом не слышали в iOS?
  2. Если это невозможно, есть ли способ перечислить все свойства в классе и написать функцию, которая сделает это автоматически.

person Jonathan    schedule 22.05.2012    source источник
comment
Я использую accessorizer для создания шаблонного кода.   -  person Matthias Bauch    schedule 22.05.2012


Ответы (1)


Вы можете использовать среду выполнения Objective-C, чтобы найти все свойства объекта и расшифровать их, но я бы не рекомендовал это делать. Если хотите, я могу создать для вас простой пример.

РЕДАКТИРОВАТЬ: Вот пример:

#import <objc/runtime.h>

void decodePropertiesOfObjectFromCoder(id obj, NSCoder *coder)
{
    // copy the property list
    unsigned propertyCount;
    objc_property_t *properties = class_copyPropertyList([obj class], &propertyCount);

    for (int i = 0; i < propertyCount; i++) {
        objc_property_t property = properties[i];

        char *readonly = property_copyAttributeValue(property, "R");
        if (readonly)
        {
            free(readonly);
            continue;
        }

        NSString *propName = [NSString stringWithUTF8String:property_getName(property)];

        @try 
        {
            [obj setValue:[coder decodeObjectForKey:propName] forKey:propName];
        }
        @catch (NSException *exception) {
            if (![exception.name isEqualToString:@"NSUnknownKeyException"])
            {
                @throw exception;
            }

            NSLog(@"Couldn't decode value for key %@.", propName);
        }
    }

    free(properties);
}

void encodePropertiesOfObjectToCoder(id obj, NSCoder *coder)
{
    // copy the property list
    unsigned propertyCount;
    objc_property_t *properties = class_copyPropertyList([obj class], &propertyCount);

    for (int i = 0; i < propertyCount; i++) {
        objc_property_t property = properties[i];

        char *readonly = property_copyAttributeValue(property, "R");
        if (readonly)
        {
            free(readonly);
            continue;
        }

        NSString *propName = [NSString stringWithUTF8String:property_getName(property)];

        @try {
            [coder encodeObject:[obj valueForKey:propName] forKey:propName];
        }
        @catch (NSException *exception) {
            if (![exception.name isEqualToString:@"NSUnknownKeyException"])
            {
                @throw exception;
            }

            NSLog(@"Couldn't encode value for key %@.", propName);
        }
    }

    free(properties);
}

__attribute__((constructor))
static void setDefaultNSCodingHandler()
{
    class_addMethod([NSObject class], @selector(encodeWithCoder:), imp_implementationWithBlock((__bridge void *)[^(id self, NSCoder *coder) {
        encodePropertiesOfObjectToCoder(self, coder);
    } copy]), "v@:@");
    class_addMethod([NSObject class], @selector(initWithCoder:), imp_implementationWithBlock((__bridge void *)[^(id self, NSCoder *coder) {
        if ((self = [NSObject instanceMethodForSelector:@selector(init)](self, @selector(init))))
        {
            decodePropertiesOfObjectFromCoder(self, coder);
        }

        return self;
    } copy]), "v@:@");
}

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

person Richard J. Ross III    schedule 22.05.2012
comment
Понимаю. Это немного больше работы, чем я думал. Почему вы не рекомендуете это? - person Jonathan; 22.05.2012
comment
@ shred444, потому что не все объекты предоставляют свои переменные как свойства, поэтому вы должны быть предупреждены, что это может не восстановить рабочую копию класса. Это также имеет немного худшую производительность, чем если бы вы делали это самостоятельно, но это незначительно. - person Richard J. Ross III; 22.05.2012