【问题标题】:Can't get NSKeyedArchiver to work: unrecognized selector [closed]无法让 NSKeyedArchiver 工作:无法识别的选择器 [关闭]
【发布时间】:2018-02-13 23:26:07
【问题描述】:

UITableView 中删除一行并保存数据源时,我还想保存到持久存储中。

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

2018-02-13 23:01:41.818945+0000 项目名称[28680:5720571] -[_SwiftValue encodeWithCoder:]:无法识别的选择器发送到实例 0x60800004d290

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

我到处搜索,找不到任何适合我的东西。据我所知,Participant 对象的数据类符合NSObjectNSCoding 并实现了必要的方法。

编辑:感谢大家这么快的反馈。代码如下,正如我之前所说,如果我忽略了发布可能与我的经验相关的必要内容,那么我们将不胜感激地收到任何帮助!

数据类(摘录)

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)
    }

}

从我试图使用它的 Private Func 内部,它中断了

   let archivePath = Participant.ArchiveURL.path
   let isSuccessfulSave = NSKeyedArchiver.archiveRootObject(participants, toFile: archivePath)

再次感谢您在这方面提供的任何帮助。

编辑 2:我做了更多的调试,我想我对 Xcode IDE 和调试的缺乏经验阻碍了这一点,但它看起来是被存储对象的 ratePeriod 属性那就是抛出错误。这个属性是一个结构,我认为其他人认为它是一个问题。这是可以解决的还是我需要寻找一种不同的方法来持久存储结构?

编辑 3:我已经解决了问题,但不知道如何将其标记为已解决。问题不在于 Struct(我没有存储它),而在于 Enum。当使用 NSKeyedArchiverNSCoding 存储 Enums 时,您需要存储 .rawValue 并将其重构为 Int。即:-

编码

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

解码

let ratePeriodIntValue = (aDecoder.decodeObject(forKey: PropertyKey.ratePeriod) as? Int) ?? ratePeriods.annually.rawValue
let ratePeriod = ratePeriods(rawValue: ratePeriodIntValue)

【问题讨论】:

  • 你使用 Git 吗?如果可以,您可以分享链接以便我查看代码吗?
  • 我冒昧地猜测您需要将对象转换为字典进行存储。但我需要查看您的代码才能确定发生了什么。
  • @Jake 如果回答问题所需的代码不在问题中,则需要在此处添加。移出网站意味着该帖子对其他人永远不会有用。
  • “据我所知,我的 Participant 对象的数据类符合 NSObject 和 NSCoding 并实现了必要的方法。”但是我们不相信你,编译器也不相信。
  • 展示您的作品或准备结束您的主题。

标签: swift nskeyedarchiver


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    有一个非常 simple article on NSHipster 关于如何使您的自定义类 NSCoding/NSKeyedArchiver 兼容。我建议你看看。

    作为一个基本答案,为了使您的对象与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")
        }
    
    }
    

    【讨论】:

      【解决方案3】:

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

      另外,请展示您是如何在 Swift 中编写 NSCoding 方法的。考虑到 Swift 和 ObjC 之间的固有转换方式,它们可能不匹配?

      func encode(with aCoder: NSCoder) {
      

      【讨论】:

        猜你喜欢
        • 2017-04-19
        • 1970-01-01
        • 2018-01-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多