Кодирайте всички свойства на 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 runtime, за да намерите всички свойства на даден обект и да ги декодирате, но аз не бих го препоръчал. Ако желаете, мога да създам прост пример за вас.

РЕДАКТИРАНЕ: Ето един пример:

#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