7

Я пытался кодировать центр MKMapView и охватывать NSKeyedArchiver для сохранения состояния. Я нашел пару полезных новых дополнений MapKit NSValue, valueWithMKCoordinate: и valueWithMKCoordinate:. Попытка кодировать их в шпоночным архиватора не удалось:Почему я не могу кодировать NSValue в NSKeyedArchiver?

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

, истекающий приложение из-за неперехваченного исключением 'NSInvalidArgumentException', причина: '- [NSKeyedArchiver encodeValueOfObjCType: в]: этот архиватор не может кодировать Структуры'

Я понимаю, что решение этой проблемы состоит в том, чтобы просто закодировать отдельные дубликаты на четыре отдельных ключа.

Мой вопрос: Почему это происходит. NSValue является объектом, так почему он говорит мне, «это архиватор не может кодировать структуры»

ответ

4

Согласно документации NSKeyedArchiver класса,

шпоночным архив отличается от не шпоночным архива в том, что все объекты и значения, закодированные в архив приведены имена, или ключи.

Чтобы архивировать элементы struct и дать ключи к ним NSKeyedArchiver бы необходимы метаданные, чтобы знать, где каждое поле из struct находится, и то, что имена этих полей. @encode, хранящийся с NSValue, дает достаточно информации о макете struct, но информация об именах каждого поля отсутствует.

Поскольку нет метаданных об именах полей в struct, было бы невозможно архивировать данные таким образом, чтобы обеспечить надлежащее хранение в архиве. Вот почему NSKeyedArchiver должен отказаться от архива NSValue s со встроенным C struct s.

+0

Я предполагал, что 'NSValue' закодировал свой материал, как' NSData', как поток байтов, который можно было распаковать на другом конце, если вы знали формат. Думаю, это неправильно? –

+0

Это не так, но NSValue не знает достаточно информации о хранящихся данных, чтобы иметь возможность упаковывать и распаковывать его в/из байтов. –

+1

@nevanking Это абсолютно верно, если вы используете 'NSArchiver'.Тем не менее, 'NSKeyedArchiver' добавляет дополнительные требования к элементам данных, чтобы иметь имена. В свою очередь, ключевой архиватор [позволяет вам распаковывать объекты, которые не являются на 100% идентичными) (https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Archiving/Articles/compatibility.html#//apple_ref/DOC/UID/20001055-BCICFFGE). – dasblinkenlight

1

NSValue используется для инкапсуляции (например, C структуры, INTS и т.д.) не-объекта в Objective-C объект, но его не предоставляет способ определения пользовательских процедур архивирования/сериализации для этих завернутых типов. Это просто не часть его интерфейса.

Если вы хотите заархивировать NSValue, содержащую структуру, вам придется принимать во внимание такие вещи, как конкретизация, и обрабатывать такие вещи, как вложенные указатели или другие типы, которые не могут быть тривиально записаны в виде байтов. Нет никакого способа сделать это автоматически с NSValue.

+0

Я думаю, именно поэтому вы можете использовать значение внутри (например, в 'NSArray '), но не внешне (например, совместное использование архива). –

2

Я столкнулся с той же проблемой, и решил, что самое простое решение было бы просто быть скучными и кодировать все значения в отдельности:

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