无法解码类的对象

我想发送一个“类”到我的Watchkit扩展,但我得到这个错误。

*由于未捕获的exception“NSInvalidUnarchiveOperationException”,原因:'* – [NSKeyedUnarchiver decodeObjectForKey:]:无法解码类的对象(MyApp.Person)

存档和取消存档在iOS应用程序中工作正常,但不与Watchkit扩展进行通信。 怎么了?

InterfaceController.swift

let userInfo = ["method":"getData"] WKInterfaceController.openParentApplication(userInfo, reply: { (userInfo:[NSObject : AnyObject]!, error: NSError!) -> Void in println(userInfo["data"]) // prints <62706c69 7374303... if let data = userInfo["data"] as? NSData { if let person = NSKeyedUnarchiver.unarchiveObjectWithData(data) as? Person { println(person.name) } } }) 

AppDelegate.swift

 func application(application: UIApplication!, handleWatchKitExtensionRequest userInfo: [NSObject : AnyObject]!, reply: (([NSObject : AnyObject]!) -> Void)!) { var bob = Person() bob.name = "Bob" bob.age = 25 reply(["data" : NSKeyedArchiver.archivedDataWithRootObject(bob)]) return } 

Person.swift

 class Person : NSObject, NSCoding { var name: String! var age: Int! // MARK: NSCoding required convenience init(coder decoder: NSCoder) { self.init() self.name = decoder.decodeObjectForKey("name") as! String? self.age = decoder.decodeIntegerForKey("age") } func encodeWithCoder(coder: NSCoder) { coder.encodeObject(self.name, forKey: "name") coder.encodeInt(Int32(self.age), forKey: "age") } } 

注:虽然这个答案中的信息是正确的,更好的答案是由@agy下面的答案。

这是由编译器从同一个类创buildMyApp.PersonMyAppWatchKitExtension.Person引起的。 这通常是由两个目标共享相同的类而不是创build一个框架来共享。

两个修复:

正确的解决方法是将Person提取到框架中。 主应用程序和watchkit扩展应该使用框架,并将使用相同的*.Person类。

解决方法是在保存并传递它之前,将您的类序列化为Foundation对象(如NSDictionary )。 NSDictionary将代码和解码跨应用程序和扩展。 一个很好的方法是在Person上实现RawRepresentable协议。

根据与Objective-C API的交互 :

当你在Swift类中使用@objc( name )属性时,这个类在Objective-C中是没有任何命名空间的。 因此,将可归档的Objective-C类迁移到Swift时,此属性也可能很有用。 由于归档对象在归档中存储了类的名称,因此应该使用@objc( name )属性指定与您的Objective-C类相同的名称,以便旧的归档可以由新的Swift类取消归档。

即使我们正在使用Swift,通过添加注释@objc(name) ,名称空间也会被忽略。 我们来演示一下。 设想目标A定义了三个类:

 @objc(Adam) class Adam:NSObject { } @objc class Bob:NSObject { } class Carol:NSObject { } 

如果目标B调用这些类:

 print("\(Adam().classForCoder)") print("\(Bob().classForCoder)") print("\(Carol().classForCoder)") 

输出将是:

 Adam B.Bob B.Carol 

但是,如果目标A调用这些类,结果将是:

 Adam A.Bob A.Carol 

要解决您的问题,只需添加@objc(name)指令:

 @objc(Person) class Person : NSObject, NSCoding { var name: String! var age: Int! // MARK: NSCoding required convenience init(coder decoder: NSCoder) { self.init() self.name = decoder.decodeObjectForKey("name") as! String? self.age = decoder.decodeIntegerForKey("age") } func encodeWithCoder(coder: NSCoder) { coder.encodeObject(self.name, forKey: "name") coder.encodeInt(Int32(self.age), forKey: "age") } } 

设置框架以使NSKeyedUnarchiver正常工作后,必须添加以下几行。

在未存档之前:

 NSKeyedUnarchiver.setClass(YourClassName.self, forClassName: "YourClassName") 

归档之前:

 NSKeyedArchiver.setClassName("YourClassName", forClass: YourClassName.self) 

我有类似的情况,我的应用程序使用我的Core框架,其中我保留所有模型类。 例如我使用NSKeyedArchiverNSKeyedUnarchiver存储和检索UserProfile对象,当我决定将我所有的类移动到MyApp NSKeyedUnarchiver开始抛出错误,因为存储的对象像Core.UserProfile而不是由unarchiver预期的MyApp.UserProfile 。 我如何解决它是创build一个NSKeyedUnarchiver的子类和重写classforClassName函数:

 class SKKeyedUnarchiver: NSKeyedUnarchiver { override open func `class`(forClassName codedName: String) -> Swift.AnyClass? { let lagacyModuleString = "Core." if let range = codedName.range(of: lagacyModuleString), range.lowerBound.encodedOffset == 0 { return NSClassFromString(codedName.replacingOccurrences(of: lagacyModuleString, with: "")) } return NSClassFromString(codedName) } } 

然后将@objc(name)添加到需要归档的类中,如其中一个答案所示。

并像这样调用它:

 if let unarchivedObject = SKKeyedUnarchiver.unarchiveObject(withFile: UserProfileServiceImplementation.archiveURL.path) as? UserProfile { currentUserProfile = unarchivedObject } 

它工作得很好。

解决schemeNSKeyedUnarchiver.setClass(YourClassName.self, forClassName: "YourClassName")的解决scheme不适合我,因为它不适用于嵌套对象,例如UserProfile具有var address: Address 。 Unarchiver将通过UserProfile获得成功,但当它进入Address更深层时将失败。

而为什么@objc(name)解决scheme本身并没有为我做的是因为我没有从OBJ-C移动到Swift,所以问题不是UserProfile – > MyApp.UserProfile ,而是Core.UserProfile – > MyApp.UserProfile