无法使NSKeyedArchiver工作:无法识别的选择器

时间:2018-02-13 23:26:07

标签: swift nskeyedarchiver

'/\[\.{2}([^\.].+?)\]/'删除行并保存数据源时,我还想保存到持久存储。

我使用UITableView保存我的自定义对象类型NSKeyedArchiver的数组,但它立即失败:

  

2018-02-13 23:01:41.818945 + 0000 ProjectName [28680:5720571]    - [_ SwiftValue encodeWithCoder:]:无法识别的选择器发送到实例0x60800004d290

     

2018-02-13 23:01:41.826553 + 0000 ProjectName [28680:5720571] ***由于未被捕获而终止应用   异常' NSInvalidArgumentException',原因:' - [_ SwiftValue   encodeWithCoder:]:发送到实例的无法识别的选择器   0x60800004d290'

我到处搜索过,无法找到适用于我的任何内容。至于我可以告诉Participant对象的数据类符合ParticipantNSObject,并且已经实现了必要的方法。

编辑:非常感谢大家的反馈意见。代码如下,正如我之前所说的,如果我忽略了发布可能与我的经验相关的必要内容,那么感谢任何帮助!

数据类(摘录)

NSCoding

来自Private Func内部,我试图利用它并打破

class Participant: NSObject, NSCoding {

    //MARK: Properties
    var name: String
    var jobTitle: String?
    var picture: UIImage?
    var rate: Float
    var ratePeriod: ratePeriods

   //MARK: Archiving Paths

    static let DocumentsDirectory = FileManager().urls(for: .documentDirectory, in: .userDomainMask).first!
    static let ArchiveURL = DocumentsDirectory.appendingPathComponent("participants")

    //MARK: Types

    struct PropertyKey {
        //THESE MUST NOT CHANGE ONCE BUILT
        static let name = "name"
        static let jobTitle = "jobTitle"
        static let picture = "picture"
        static let rate = "rate"
        static let ratePeriod = "ratePeriod"
    }

    //MARK: Initialisation

    init?(name: String, jobTitle: String?, picture: UIImage?, rate: Float?, ratePeriod: ratePeriods?) {

        //The name must not be empty
        guard !name.isEmpty else {
            return nil
        }

        //Init stored properties
        self.name = name
        self.jobTitle = jobTitle ?? ""
        self.picture = picture
        self.rate = rate ?? 0.0
        self.ratePeriod = ratePeriod ?? .annually
    }

    //MARK: NSCoding
    func encode(with aCoder: NSCoder) {
        aCoder.encode(name, forKey: PropertyKey.name)
        aCoder.encode(jobTitle, forKey: PropertyKey.jobTitle)
        aCoder.encode(picture, forKey: PropertyKey.picture)
        aCoder.encode(rate, forKey: PropertyKey.rate)
        aCoder.encode(ratePeriod, forKey: PropertyKey.ratePeriod)
    }

    required convenience init?(coder aDecoder: NSCoder) {
        //The name is required. If we cannot decode a name string, the init should fail.
        guard let name = aDecoder.decodeObject(forKey: PropertyKey.name) as? String else {
            return nil
        }

        let jobTitle = aDecoder.decodeObject(forKey: PropertyKey.jobTitle) as? String
        let picture = aDecoder.decodeObject(forKey: PropertyKey.picture) as? UIImage
        let rate = aDecoder.decodeFloat(forKey: PropertyKey.rate)
        let ratePeriod = aDecoder.decodeObject(forKey: PropertyKey.ratePeriod) as? ratePeriods


        //Must call designated init
        self.init(name: name, jobTitle: jobTitle, picture: picture, rate: rate, ratePeriod: ratePeriod)
    }

}

再次感谢您对我的帮助。

编辑2 :我已经完成了一些调试,我猜我对Xcode IDE和调试的经验不足阻碍了这一点,但它看起来是 let archivePath = Participant.ArchiveURL.path let isSuccessfulSave = NSKeyedArchiver.archiveRootObject(participants, toFile: archivePath) 属性存储的对象抛出错误。这个属性是一个结构,我认为其他人倾向于作为一个问题。这是可解决的还是我需要查看另一种持久存储Structs的方法?

编辑3 :我已经解决了问题,但不知道如何将其标记为已解决。问题不是结构(我没有存储),而是 Enum 。使用ratePeriodNSKeyedArchiver存储枚举时,您需要存储NSCoding并将其重新组合为Int。即: -

编码

.rawValue

解码

aCoder.encode(ratePeriod.rawValue, forKey: PropertyKey.ratePeriod)

3 个答案:

答案 0 :(得分:1)

您的自定义对象类型Participant需要是一个类(不是结构),特别是它必须是NSObject的子类。现在您可以成功采用NSCoding。

答案 1 :(得分:0)

关于如何使自定义类NSCoding / NSKeyedArchiver合规,有一个非常simple article on NSHipster。我建议你看看。

作为基本答案,为了使您的对象与NSKeyedArchiver一起使用,您需要告诉编码器/解码器如何编码/解码您的对象。例如,如果这是您的班级:

class Participant {

    let name: String
    let id: String
    let age: Int

    init(name: String, id: String, age: Int) {
        self.name = name
        self.id = id
        self.age = age
    }

}

您需要进行以下修改才能使其与NSKeyedArchiver一起使用。首先,声明您符合NSObjectNSCoding协议。然后实施encode方法和convenience init?(coder:_)初始化程序:

class Participant: NSObject, NSCoding {

    let name: String
    let id: String
    let age: Int

    init(name: String, id: String, age: Int) {
        self.name = name
        self.id = id
        self.age = age
    }

    required convenience init?(coder aDecoder: NSCoder) {
        guard let name = aDecoder.decodeObject(forKey: "name") as? String,
            let id = aDecoder.decodeObject(forKey: "id") as? String,
            let age = aDecoder.decodeObject(forKey: "age") as? Int else { return nil }

        self.init(name: name, id: id, age: age)
    }

    func encode(with aCoder: NSCoder) {
        aCoder.encode(self.name, forKey: "name")
        aCoder.encode(self.id, forKey: "id")
        aCoder.encode(self.age, forKey: "age")
    }

}

答案 2 :(得分:-1)

尝试将@objc添加到swift等效的encodeWithCoder:方法中。同上解码init方法。我怀疑这是解决方案,但可能是。

另外,请说明如何在Swift中编写NSCoding方法。考虑到Swift和ObjC之间关于如何编写它们的内在转换,它们可能不匹配?

func encode(with aCoder: NSCoder) {