7

Ich habe versucht, ein MKMapView Center zu codieren und in eine NSKeyedArchiver für die Erhaltung des Zustands spannen. Ich fand ein paar handliche neue MapKit NSValue Ergänzungen, valueWithMKCoordinate: und valueWithMKCoordinate:. Der Versuch, diese in die Schlüsselarchivierungs zu kodieren fehlgeschlagen:Warum kann ich keinen NSValue in einem NSKeyedArchiver codieren?

- (void)encodeRestorableStateWithCoder:(NSCoder *)coder 
{ 
    NSValue *mapCenterValue = [NSValue valueWithMKCoordinate:mapView.centerCoordinate]; 
    NSValue *mapSpanValue = [NSValue valueWithMKCoordinateSpan:mapView.region.span]; 
    [coder encodeObject:mapCenterValue forKey:kMapCenter]; 
    [coder encodeObject:mapSpanValue forKey:kMapSpan]; 
} 

App beenden aufgrund nicht abgefangene Ausnahme 'NSInvalidArgumentException', Grund: '- [NSKeyedArchiver encodeValueOfObjCType: in:]: Der Archivierungs kodieren, kann nicht structs'

Ich verstehe, dass die Lösung für dieses Problem ist, nur die einzelnen Doubles in vier separate Schlüssel zu kodieren.

Meine Frage ist warum geschieht dies. Ein NSValue ist ein Objekt, also warum ist es mir zu sagen, „dieses Archivierungs kann structs nicht kodieren“

Antwort

4

Nach der Dokumentation der NSKeyedArchiver Klasse,

Codierter-Archiv unterscheidet sich von einer nicht-verschlüsselten Archiv, dass alle Objekte und Werte in das Archiv codierten Namen gegeben sind, oder Tasten.

Um Elemente einer struct und geben Schlüssel zu ihnen NSKeyedArchiver Metadaten, wobei jedes Feld eines struct befindet wissen müssen, würde zu archivieren ist, und was sind die Namen dieser Felder. Die @encode gespeichert mit NSValue gibt es genügend Informationen über das Layout einer struct, aber die Informationen über die Namen der einzelnen Felder fehlen.

Da es keine Metadaten zu den Namen der Felder in einer struct gibt, wäre es unmöglich, die Daten so zu archivieren, dass eine ordnungsgemäße Archivierung gewährleistet ist. Deshalb muss NSKeyedArchiver die Archivierung NSValue s mit eingebetteten C struct s verweigern.

+0

Ich hatte angenommen, dass 'NSValue' seine Dateien wie' NSData' codiert hat, als ein Strom von Bytes, die am anderen Ende entpackt werden könnten, wenn Sie das Format kannten. Ich denke, das ist falsch? –

+0

Es ist nicht falsch, aber NSValue weiß nicht genug Informationen über die Daten, die es speichert, um es in/aus Bytes zu packen und zu entpacken. –

+1

@nevanking Das ist absolut richtig, wenn Sie 'NSArchiver' verwenden."NSKeyedArchiver" stellt jedoch eine zusätzliche Anforderung an die Datenelemente, um Namen zu haben. Im Gegenzug ermöglicht der "Keyed Archiver" [das Auslagern von Objekten, die nicht zu 100% identisch sind] (https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Archiving/Articles/compatibility.html#//apple_ref/doc/uid/20001055-BCICFFGE). – dasblinkenlight

1

NSValue verwendet wird, nicht-Objekt (zB C structs, Ints, etc.) in ein Objective-c-Objekt zu kapseln, aber es bietet keine Möglichkeit, benutzerdefinierte Archivierungs-/Serialisierungsroutinen für diese umschlossenen Typen zu definieren. Dies ist nur nicht Teil seiner Schnittstelle.

Wenn Sie einen NSValue mit einer Struktur archivieren möchten, müssten Sie Dinge wie Endianität in Betracht ziehen und Dinge wie verschachtelte Zeiger oder andere Typen behandeln, die nicht einfach als Bytes geschrieben werden können. Es gibt keine Möglichkeit, dies automatisch mit NSValue zu tun.

+0

Ich denke, das ist, warum Sie Werte intern verwenden können (wie in einem 'NSArray ') aber nicht extern (wie das Teilen eines Archivs). –

2

Ich lief in das gleiche Problem und beschlossen, die einfachste Lösung wäre einfach langweilig sein, und kodieren alle Werte individuell:

- (void)encodeRestorableStateWithCoder:(NSCoder *)coder 
{ 
    MKCoordinateRegion region = [self.mapView region]; 
    [coder encodeDouble:region.center.latitude forKey:RWSMapCenterLatitude]; 
    [coder encodeDouble:region.center.longitude forKey:RWSMapCenterLongitude]; 
    [coder encodeDouble:region.span.latitudeDelta forKey:RWSMapCenterLatitudeDelta]; 
    [coder encodeDouble:region.span.longitudeDelta forKey:RWSMapCenterLongitudeDelta]; 

    [super encodeRestorableStateWithCoder:coder]; 
} 

- (void)decodeRestorableStateWithCoder:(NSCoder *)coder 
{ 
    MKCoordinateRegion region; 
    CLLocationCoordinate2D center; 
    center.latitude = [coder decodeDoubleForKey:RWSMapCenterLatitude]; 
    center.longitude = [coder decodeDoubleForKey:RWSMapCenterLongitude]; 
    region.center = center; 
    MKCoordinateSpan span; 
    span.latitudeDelta = [coder decodeDoubleForKey:RWSMapCenterLatitudeDelta]; 
    span.longitudeDelta = [coder decodeDoubleForKey:RWSMapCenterLongitudeDelta]; 
    region.span = span; 

    self.mapView.region = region; 

    [super decodeRestorableStateWithCoder:coder]; 
}