2012-09-05 9 views
6

Tôi không hiểu tại sao tôi có thể lưu trữ cấu trúc CGPoint nhưng không được cấu trúc CLLocationCoordinate2D. Sự khác biệt với kho lưu trữ là gì?NSKeyedArchiver không thành công với cấu trúc CLLocationCoordinate2D. Tại sao?

Nền tảng là iOS. Tôi đang chạy trong trình mô phỏng và chưa thử trên thiết bị.

// 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" ]; 

tôi nhận được một vụ tai nạn vào ngày 2 archiveRootObject và thông điệp này được in ra cửa sổ Console:

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

Trả lời

18

OK, Tom, bạn đã sẵn sàng cho một chút đam mê? Tôi là một anh chàng "lớn tuổi" trong thế giới của những kẻ bắt cóc trẻ tuổi. Tuy nhiên, tôi nhớ một vài điều về C, và tôi chỉ là một người đam mê.

Dù sao, có một sự khác biệt tinh tế giữa này:

typedef struct { double d1, d2; } Foo1; 

và điều này:

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

Đầu tiên là một loại bí danh đến một cấu trúc vô danh. Thứ hai là bí danh loại cho struct Foo2.

Bây giờ, tài liệu cho @encode nói rằng sau:

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

sẽ dẫn đến {[email protected]*i} cho cả @encode(example) hoặc @encode(Example). Vì vậy, điều này ngụ ý rằng @encode đang sử dụng thẻ cấu trúc thực tế. Trong trường hợp của một typedef tạo ra một bí danh cho một struct nặc danh, có vẻ như @encode luôn trả ? '

Kiểm tra này ra:

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

Dù sao, bạn có thể đoán như thế nào CLLocationCoordinate2D được xác định? Vâng. Bạn đoán nó.

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

Tôi nghĩ bạn nên gửi báo cáo lỗi về vấn đề này. Hoặc là @encode bị hỏng vì nó không sử dụng bí danh typedefs cho các cấu trúc ẩn danh, hoặc CLLocationCoordinate2D cần được gõ đầy đủ để nó không phải là một cấu trúc ẩn danh.

+0

Cảm ơn; đó là lời giải thích hoàn hảo! Tôi sẽ tham khảo nó trong báo cáo radar của tôi :) – TomSwift

0

Đó là vì @encode cuộn cảm trên CLLocationCoordinate2D

NSLog(@"coords %@; type: %s", coords, @encode(CLLocationCoordinate2D)); sản lượng coords ( "<00000000 00405e40 00000000 00804440>" ); type: {?=dd}

+1

okay, đó là thú vị. vì vậy @encode được rằng đó là một cấu trúc với hai đôi nhưng bỏ tên loại. Làm thế nào mà? Và thực sự, tại sao tên tệp lại quan trọng đối với cấu trúc được mã hóa? – TomSwift

+0

Tôi nghĩ rằng mã hóa tạo ra một chuỗi được sử dụng để làm một cái gì đó như '[[NSClassFromString (@encode (name)) alloc] init]' hoặc một số phép thuật đen tương tự để tìm hàm tạo 'CLLocationCoord' hoặc' NSPoint' hoặc whatnot . – Clay

3

Để khắc phục hạn chế này cho đến khi lỗi được cố định, chỉ cần phá vỡ các tọa độ và tái tạo:

- (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 }; 
    ...