NSInvalidUnarchiveOperationException仅在iOS扩展中引发,而不是主应用程序

时间:2015-03-23 00:06:37

标签: ios file cocoa-touch swift nskeyedarchiver

我已将符合NSCoding的类“Assignment”的数组存档到共享(App Groups)容器中的文件:

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是什么,但由于某种原因无法解码它。 作为附录,这里是分配的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>
    }

2 个答案:

答案 0 :(得分:2)

我有完全相同的问题。我试图删除并从编译源添加文件。也不工作。你找到了解决方案吗?

唯一不同于我的问题:我尝试取消归档NSArray对象xxx。它适用于在主应用程序中归档和取消归档,或在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
}

我还在主app的applicationFinishLaunchingWithOptions和WatchKitApp的第一个WKInterfaceController中设置了NSKeyedUnarchiver和NSKeyedArchiver的className

    NSKeyedArchiver.setClassName("Song", forClass: Song.self)
    NSKeyedUnarchiver.setClass(Song.self, forClassName: "Song")

答案 1 :(得分:0)

我也遇到了完全相同的问题。 总结以前的答案和评论,解决方案在于 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)
    }
}