Как я могу декодировать объект, когда исходный класс недоступен?

У меня есть приложение iOS 7, которое сохраняет пользовательский объект в папку приложения iCloud Docs в виде файла. Для этого я использую протокол NSCoding.

@interface Person : NSObject <NSCoding>

    @property (copy, nonatomic) NSString *name
    @property (copy, nonatomic) NSString *lastName

@end

Сериализация объектов отлично работает в версии приложения iOS 7:

  1. initWithCoder и encodeWithCoder

  2. [NSKeyedArchiver archivedDataWithRootObject:person]

  3. person = NSKeyedUnarchiver unarchiveObjectWithData:(NSData *)theData]

Но мне нужно переместить это приложение в iOS 8, и этот класс будет закодирован в быстрой и "переименован" для этой новой версии приложения iOS 8.

class PersonOldVersion: NSObject, NSCoding {
    var name = ""
    var lastName = ""
}

Когда я пытаюсь разархивировать объект, я получил следующую ошибку:

*** Terminating app due to uncaught exception 'NSInvalidUnarchiveOperationException', reason: '*** -[NSKeyedUnarchiver decodeObjectForKey:]: cannot decode object of class (Person)'

Я уже пробовал переименовать swift класс PersonOldVersion в исходное имя класса ("Person"), но все еще не удается.

Как я могу декодировать объект, оригинальный класс которого недоступен?

Ответ 1

Если имя класса в Objective-C важно, вам нужно явно указать имя. В противном случае Swift предоставит некоторое искаженное имя.

@objc(Person)
class PersonOldVersion: NSObject, NSCoding {
    var name = ""
    var lastName = ""
}

Ответ 2

Это может быть другое решение, и это то, что я сделал (поскольку я не использую Swift).

В моем случае я архивировал объект класса "Город", но затем переименовал класс в "CityLegacy", потому что создал совершенно новый класс "Город".

Я должен был сделать это, чтобы удалить старый объект "Город" как объект "CityLegacy":

// Tell the NSKeyedUnarchiver that the class has been renamed
[NSKeyedUnarchiver setClass:[CityLegacy class] forClassName:@"City"];

// Unarchive the object as usual
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSData *data = [defaults objectForKey:@"city"];
CityLegacy *city = [NSKeyedUnarchiver unarchiveObjectWithData:data];

Ответ 3

Здесь приведен быстрый перевод для Феррана Мэйлинча выше.

Имел аналогичную проблему в проекте Swift после того, как я продублировал исходную цель, чтобы иметь 2 сборки моего продукта. 2 сборки необходимо использовать взаимозаменяемо.

Итак, у меня было что-то вроде myapp_light.app и моего app_pro.app. Установка класса исправила эту проблему.

NSKeyedUnarchiver.setClass(MyClass1.classForKeyedUnarchiver(), forClassName: "myapp_light.MyClass1")
NSKeyedUnarchiver.setClass(MyClass1.classForKeyedUnarchiver(), forClassName: "myapp_pro.MyClass1")



if let object:AnyObject = NSKeyedUnarchiver.unarchiveObjectWithFile("\path\...") {
    var myobject = object as! Dictionary<String,MyClass1>
    //-- other stuff here
}

Ответ 4

Я думаю, вы могли бы решить это следующим образом:

@implementation PersonOldVersion

+ (void)load
{
    [NSKeyedUnarchiver setClass:[PersonOldVersion class] forClassName:@"Person"];
}

Ответ 5

Ответ newacct помог мне с именем класса, но у меня также была проблема, когда я изменил имя переменной в моем классе Swift из моего класса Objective-C. С помощью @objc() исправлено это также:

Старый класс:

@interface JALProgressData : NSObject <NSCoding>

@property (nullable, nonatomic, strong) NSString *platformID;
@property (nonatomic, assign) NSTimeInterval secondsPlayed;
@property (nonatomic, assign) NSTimeInterval totalDuration;
@property (nullable, nonatomic, strong) NSDate *lastUpdated;

@end

Новый класс:

@objc(JALProgressData)
class VideoProgress: NSObject, NSCoding {
    @objc(platformID) var videoId: String?
    var secondsPlayed: NSTimeInterval?
    var totalDuration: NSTimeInterval?
    var lastUpdated: NSDate?
}

Я также понял, что я использовал encodeObject decodeObjectForKey для типов значений, что вызывало проблемы с моим классом Swift. Переключение на encodeDouble и decodeDoubleForKey для NSTimeInterval типов фиксированного aDecoder возвращающихся nil для этих значений.