NSInvalidUnarchiveOperationException 仅在 iOS 扩展中引发,而不是主应用程序
Posted
技术标签:
【中文标题】NSInvalidUnarchiveOperationException 仅在 iOS 扩展中引发,而不是主应用程序【英文标题】:NSInvalidUnarchiveOperationException raised only in iOS extension, not main app 【发布时间】:2015-03-23 00:06:36 【问题描述】:我已将符合 NSCoding 的类“分配”的数组归档到共享(应用程序组)容器中的文件中:
class private func updateSharedFiles()
let path = NSFileManager().containerURLForSecurityApplicationGroupIdentifier("group.agenda-touch")!.path! + "/widgetData"
if let incompleteAssignmentsArray = self.incompleteAssignments
NSKeyedArchiver.archiveRootObject(incompleteAssignmentsArray, toFile: path)
现在,当我的扩展程序想要从该文件中读取时,我在该文件上调用了 NSFileManager.fileExistsAtPath: 并且它返回 true。最后我打电话给NSKeyedUnarchiver.unarchiveObjectWithFile(path) as? AssignmentArray
并得到一个 NSInvalidUnarchiveOperationException。问题是,当我在我的主应用程序中解压缩文件时,我没有遇到这样的异常。需要明确的是,我已将 Assignment.swift 添加到小部件的编译源中。所以我的 TodayViewController 知道 Assignment 是什么,但由于某种原因无法对其进行解码。
作为附录,这里是 Assignment 的 NSCoding 实现:
//MARK: NSCoding
func encodeWithCoder(aCoder: NSCoder)
aCoder.encodeObject(self.assignmentName, forKey: "assignmentName")
aCoder.encodeObject(self.assignmentDueDate, forKey: "assignmentDueDate")
aCoder.encodeObject(self.assignmentSubject, forKey: "assignmentSubject")
aCoder.encodeBool(self.completed, forKey: "assignmentCompleted")
aCoder.encodeObject(self.creationDate, forKey: "assignmentCreationDate")
aCoder.encodeInteger(self.assignmentType.rawValue, forKey: "assignmentType")
aCoder.encodeBool(self.earmarkedForDeletion, forKey: "assignmentEarmarked")
aCoder.encodeObject(self.modificationDates, forKey: "assignmentModificationDates")
required convenience init(coder aDecoder: NSCoder)
let assignmentName = aDecoder.decodeObjectForKey("assignmentName") as String
let assignmentDueDate = aDecoder.decodeObjectForKey("assignmentDueDate") as NSDate
let assignmentSubject = aDecoder.decodeObjectForKey("assignmentSubject") as String
let completed = aDecoder.decodeBoolForKey("assignmentCompleted")
self.init(name:assignmentName,dueDate:assignmentDueDate,subject:assignmentSubject,completed:completed)
self.creationDate = aDecoder.decodeObjectForKey("assignmentCreationDate") as NSDate
let assignmentTypeRaw = aDecoder.decodeIntegerForKey("assignmentType")
self.assignmentType = AssignmentType(rawValue: assignmentTypeRaw)!
self.earmarkedForDeletion = aDecoder.decodeBoolForKey("assignmentEarmarked")
self.modificationDates = aDecoder.decodeObjectForKey("assignmentModificationDates") as Dictionary<String,NSDate>
【问题讨论】:
【参考方案1】:我也有同样的问题。我试图从编译源中删除和添加文件。也不工作。你找到解决方案了吗?
与我的问题的唯一区别:我尝试取消归档对象 xxx 的 NSArray。当在主应用程序中存档和取消存档或在 WatchKit 应用程序中存档和取消存档时,它可以工作,但当我在主应用程序中存档并在 WatchKit 应用程序中取消存档时(或其他)则不起作用。
let defaults: NSUserDefaults = NSUserDefaults(suiteName: "group.name")!
if let object: NSData = defaults.objectForKey(ArrayKey) as? NSData
if let array: AnyObject = NSKeyedUnarchiver.unarchiveObjectWithData(object)
return array as NSArray
return NSArray()
通过我的应用程序组中的 NSUserDefaults 交换对象有效。我只遇到带有对象 xxx 的 NSArray 的 NSKeyedArchiver 和 NSKeyedUnarchiver 的问题。
* 由于未捕获的异常 'NSInvalidUnarchiveOperationException' 导致应用程序终止,原因:'* -[NSKeyedUnarchiver decodeObjectForKey:]:无法解码类 xxx 的对象
编辑:我解决了使我的对象符合 NSSecureCoding 的问题。因此我更换了
propertyName = aDecoder.decodeObjectForKey("propertyName")
与
propertyName = aDecoder.decodeObjectOfClass(NSString.self, forKey: "propertyName") as NSString
并添加了
class func supportsSecureCoding() -> Bool
return true
我还在主应用的 applicationFinishLaunchingWithOptions 和 WatchKitApp 的第一个 WKInterfaceController 中设置了 NSKeyedUnarchiver 和 NSKeyedArchiver 的 className
NSKeyedArchiver.setClassName("Song", forClass: Song.self)
NSKeyedUnarchiver.setClass(Song.self, forClassName: "Song")
【讨论】:
等等,我不认为这是一个答案。 对不起,由于声誉低,我无法对原始问题发表评论。 :) 我编辑了帖子,所以它可能包含原始问题的答案。 只需致电NSKeyedArchiver.setClassname
和NSKeyedUnarchiver.setClass
。采用NSSecureCoding
应该没有任何区别。根本问题是存储的类名包括模块,并且您的扩展程序和主应用程序具有不同的模块名。【参考方案2】:
我也遇到了同样的问题。
从之前的答案和 cmets 总结,解决方案在于 setClass
/setClassName
语句。我在下面的示例代码(Swift 5)中将我的编码包装为函数:
import Foundation
public class User: NSObject, NSSecureCoding
public var id: String
public var name: String
public init(id: String, name: String)
self.id = id
self.name = name
// NSSecureCoding
public static var supportsSecureCoding: Bool return true
public func encode(with coder: NSCoder)
coder.encode(id , forKey: "id")
coder.encode(name, forKey: "name")
public required init?(coder: NSCoder)
guard
let id = coder.decodeObject(forKey: "id") as? String,
let name = coder.decodeObject(forKey: "name") as? String
else
return nil
self.id = id
self.name = name
// (Un)archiving
public static func decode(from data: Data) -> Self?
NSKeyedUnarchiver.setClass(Self.self, forClassName: String(describing: Self.self))
return try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? Self
public func encode() -> Data?
NSKeyedArchiver.setClassName(String(describing: Self.self), for: Self.self)
return try? NSKeyedArchiver.archivedData(withRootObject: self, requiringSecureCoding: Self.supportsSecureCoding)
【讨论】:
以上是关于NSInvalidUnarchiveOperationException 仅在 iOS 扩展中引发,而不是主应用程序的主要内容,如果未能解决你的问题,请参考以下文章