问题
I have an iOS 7 application that saves a custom object to app's iCloud Docs folder as a file. For this, I make use of NSCoding protocol.
@interface Person : NSObject <NSCoding>
@property (copy, nonatomic) NSString *name
@property (copy, nonatomic) NSString *lastName
@end
Object serialization works perfectly in iOS 7 version of the app:
initWithCoder
andencodeWithCoder
[NSKeyedArchiver archivedDataWithRootObject:person]
person = NSKeyedUnarchiver unarchiveObjectWithData:(NSData *)theData]
But I need to move this app to iOS 8, and this class will be coded in swift and 'renamed' for this new iOS 8 version of the app.
class PersonOldVersion: NSObject, NSCoding {
var name = ""
var lastName = ""
}
When I try to unarchive the object I got the following error:
*** Terminating app due to uncaught exception 'NSInvalidUnarchiveOperationException', reason: '*** -[NSKeyedUnarchiver decodeObjectForKey:]: cannot decode object of class (Person)'
I already tried renaming swift class 'PersonOldVersion' to original class name ('Person') but still fails.
How can I decode an object which original class isn't available?
回答1:
If the name of the class in Objective-C is important, you need to explicitly specify the name. Otherwise, Swift will provide some mangled name.
@objc(Person)
class PersonOldVersion: NSObject, NSCoding {
var name = ""
var lastName = ""
}
回答2:
This might be another solution, and it's what I did (since I don't use Swift).
In my case, I archived an object of class "City" but then renamed the class to "CityLegacy" because I created a completely new "City" class.
I had to do this to unarchive the old "City" object as a "CityLegacy" object:
// 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:
Here's a Swift translation for Ferran Maylinch's answer above.
Had a similar problem in a Swift project after I duplicated the original target in order to have 2 builds of my product. The 2 builds needed to be useable interchangeably.
So, I had something like myapp_light.app and my app_pro.app. Setting the class fixed this issue.
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:
I think you could resolve it as follow:
@implementation PersonOldVersion
+ (void)load
{
[NSKeyedUnarchiver setClass:[PersonOldVersion class] forClassName:@"Person"];
}
回答5:
newacct's answer helped me with the class name, but I also had an issue where I changed a variable name in my Swift class from my Objective-C class. Using @objc()
fixed that as well:
Old Class:
@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
New Class:
@objc(JALProgressData)
class VideoProgress: NSObject, NSCoding {
@objc(platformID) var videoId: String?
var secondsPlayed: NSTimeInterval?
var totalDuration: NSTimeInterval?
var lastUpdated: NSDate?
}
I also realized I was using encodeObject
decodeObjectForKey
for value types, which was causing issues with my Swift class. Switching to encodeDouble
and decodeDoubleForKey
for the NSTimeInterval
types fixed aDecoder
returning nil
for those values.
来源:https://stackoverflow.com/questions/25492158/how-can-i-decode-an-object-when-original-class-is-not-available