Pergunta

I don't understand why I can archive CGPoint structs but not CLLocationCoordinate2D structs. What's the difference to the archiver?

Platform is iOS. I'm running in the simulator and haven't tried on the device.

// why does this work:
NSMutableArray *points = [[[NSMutableArray alloc] init] autorelease];
CGPoint p = CGPointMake(10, 11);
[points addObject:[NSValue valueWithBytes: &p objCType: @encode(CGPoint)]];
[NSKeyedArchiver archiveRootObject:points toFile: @"/Volumes/Macintosh HD 2/points.bin" ];

// and this doesnt work:
NSMutableArray *coords = [[[NSMutableArray alloc] init] autorelease];
CLLocationCoordinate2D c = CLLocationCoordinate2DMake(121, 41);
[coords addObject:[NSValue valueWithBytes: &c objCType: @encode(CLLocationCoordinate2D)]];
[NSKeyedArchiver archiveRootObject:coords toFile: @"/Volumes/Macintosh HD 2/coords.bin" ];

I get a crash on the 2nd archiveRootObject and this message is printed to the console:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSKeyedArchiver encodeValueOfObjCType:at:]: this archiver cannot encode structs'
Foi útil?

Solução

OK, Tom, are you ready for some geek-ness? I'm an "older" guy in this world of young whippersnappers. However, I remember a few things about C, and I'm just a geek at heart.

Anyway, there is a subtle difference between this:

typedef struct { double d1, d2; } Foo1;

and this:

typedef struct Foo2 { double d1, d2; } Foo2;

The first is a type alias to an anonymous structure. The second is a type alias to struct Foo2.

Now, the documentation for @encode says that the following:

typedef struct example {
    id   anObject;
    char *aString;
    int  anInt;
} Example;

will result in {example=@*i} for both @encode(example) or @encode(Example). So, this implies that @encode is using the actual struct tag. In the case of a typedef that creates an alias to an anonymous struct, it looks like @encode always returns ?'

Check this out:

NSLog(@"Foo1: %s", @encode(Foo1));
NSLog(@"Foo2: %s", @encode(Foo2));

Anyway, can you guess how CLLocationCoordinate2D is defined? Yep. You guessed it.

typedef struct {
CLLocationDegrees latitude;
CLLocationDegrees longitude;
} CLLocationCoordinate2D;

I think you should file a bug report on this. Either @encode is broken because it does not use alias typedefs to anonymous structs, or CLLocationCoordinate2D needs to be fully typed so it is not an anonymous struct.

Outras dicas

To get around this limitation until the bug is fixed, simply break down the coordinates and reconstruct:

- (void)encodeWithCoder:(NSCoder *)coder
{
    NSNumber *latitude = [NSNumber numberWithDouble:self.coordinate.latitude];
    NSNumber *longitude = [NSNumber numberWithDouble:self.coordinate.longitude];
    [coder encodeObject:latitude forKey:@"latitude"];
    [coder encodeObject:longitude forKey:@"longitude"];
    ...

- (id)initWithCoder:(NSCoder *)decoder
{
    CLLocationDegrees latitude = (CLLocationDegrees)[(NSNumber*)[decoder decodeObjectForKey:@"latitude"] doubleValue];
    CLLocationDegrees longitude = (CLLocationDegrees)[(NSNumber*)[decoder decodeObjectForKey:@"longitude"] doubleValue];
    CLLocationCoordinate2D coordinate = (CLLocationCoordinate2D) { latitude, longitude };
    ...

It's because @encode chokes on CLLocationCoordinate2D

NSLog(@"coords %@; type: %s", coords, @encode(CLLocationCoordinate2D)); yields coords ( "<00000000 00405e40 00000000 00804440>" ); type: {?=dd}

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top