Pregunta

I was trying to create a generic encoder and decoder for my model classes. I was trying to find a way to call the "encode method" for all types of properties, either objects (NSString, NSNumber, NSArray, etc...) and primitive types. And I saw someone doing the following. And I was wondering if this would a correct way to do it.

Properties:

@property (assign,nonatomic) int integerP;
@property (assign,nonatomic) float floatP;
@property (assign,nonatomic) BOOL boolP;

Enconder and Decoder Code:

- (void)encodeWithCoder:(NSCoder *)encoder
{
    id object2 = [self valueForKey:@"integerP"];
    id object3 = [self valueForKey:@"floatP"];
    id object4 = [self valueForKey:@"boolP"];


    [encoder encodeObject:object2 forKey:@"integerP"];
    [encoder encodeObject:object3 forKey:@"floatP"];
    [encoder encodeObject:object4 forKey:@"boolP"];

    //[self setValue:[NSNumber numberWithInt:90] forKey:@"heightR"];

    //NSLog(@"%@",[self valueForKey:@"heightR"]);


}

- (id)initWithCoder:(NSCoder *)decoder
{
    self = [super init];
    if( self != nil )
    {

        id object2 = [decoder decodeObjectForKey:@"integerP"];
        [self setValue:object2 forKey:@"integerP"];
        id object3 = [decoder decodeObjectForKey:@"floatP"];
        [self setValue:object3 forKey:@"floatP"];
        id object4 = [decoder decodeObjectForKey:@"boolP"];
        [self setValue:object4 forKey:@"boolP"];

    }
    return self;
}

I was not sure if this is a correct way, or if other program or object could write in the same memory space of the primitive properties. If the method above is correct, what is the difference between the above and this:

The way I thought was correct:

- (void)encodeWithCoder:(NSCoder *)encoder
{


    [encoder encodeInt:integerP forKey:@"integerP"];
    [encoder encodeFloat:floatP forKey:@"floatP"];
    [encoder encodeBool:boolP forKey:@"boolP"];

    //[self setValue:[NSNumber numberWithInt:90] forKey:@"heightR"];

    //NSLog(@"%@",[self valueForKey:@"heightR"]);


}

- (id)initWithCoder:(NSCoder *)decoder
{
    self = [super init];
    if( self != nil )
    {
        integerP = [decoder decodeIntForKey:@"integerP"];
        floatP = [decoder decodeFloatForKey:@"floatP"];
        boolP = [decoder decodeBoolForKey:@"boolP"];


    }
    return self;
}

I tested and both methods returned the correct values.

¿Fue útil?

Solución

Both methods will work.

The first is particularly clever, being that valueForKey: will always return an NSObject, even when the value is actually a primitive, so float/int/bool types will be wrapped in an NSNumber automatically by the KVC getter, and unwrapped in the KVC setter.

It might be possible to use this to implement some sort of generic encode/decode functions that operate on an array of property keys.

However, the second example is the standard way to do it, and the way I'd probably recommend. Sometimes you've got to write boilerplate code!

Otros consejos

Try this:

BaseModel.h

@interface BaseModel : NSObject<NSCoding>
@end

BaseModel.m

- (NSArray *)keysForEncoding
{
    [NSException raise:@"keysForEncoding" format:@"keysForEncoding must be implemented in child class!"];
    //example implementation in child class:
    //return @[@"airtime", @"channelID", @"duration", @"programID", @"shortTitle"];
    return nil;
}


-(void)encodeWithCoder:(NSCoder *)aCoder
{
    for(NSString* key in [self keysForEncoding])
    {
        [aCoder encodeObject:[self valueForKey:key] forKey:key];
    }
}

-(id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super init];
    if (self) {
        for (NSString* key in [self keysForEncoding]) {
            [self setValue:[aDecoder decodeObjectForKey:key] forKey:key];
        }
    }
    return self;
}

Then derive from that base class your class with actual data.

Example of simple class:

EPGData.h

    @interface EPGData : BaseModel
    @property(nonatomic, assign) NSTimeInterval airtime; //date in 1970 format
    @property(nonatomic, assign) int channelID;
    @property(nonatomic, assign) float duration; //in seconds
    @property(nonatomic, assign) unsigned int programID;
    @property(nonatomic, strong) NSString* shortTitle;
    @end

EPGData.m

- (NSArray *)keysForEncoding;
{
    return [NSArray arrayWithObjects:@"airtime", @"channelID", @"duration", @"programID", @"shortTitle", nil];
}

You first method looks very strange!

In Objective-C float/int/BOOL are not "Object". You can convert them into a NSNumber by calling:

NSNumber *aNumber = [NSNumber numberWithInt:integerP];

But your second solution looks fine.
Only use decodeObjectForKey for Objects like NSArray, etc. or your own class (where you also need to add the coding/decoding methods!)

And leave your fingers from setValue and valueForKey.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top