ios - 为什么 NSKeyedArchiver 和 NSKeyedUnarchiver 在这里没有按预期工作?

标签 ios swift nskeyedarchiver

我一直在做一个项目,我需要将一些对象写入磁盘,然后稍后再取回它们。我使用 NSKeyedArchiverNSKeyedUnarchiver 进行写入和读取,确保我的自定义类符合 NSCoding。它不工作,因此,在尝试调试它并搜索 Web 很长时间后,我决定找出问题所在。

有人能告诉我为什么这段代码没有按照我的预期去做吗?这可能很明显,但在盯着它看了好几个小时后,我仍然无法理解。

我在 Main.storyboard 中定义了一个 View Controller ,其中包含一个 UILabel、一个 UITextField 和一个 UIButton。这个想法是用户可以输入一些东西,按下“发布”按钮,标签将显示他们输入的内容。每次用户输入内容时,我希望将其写入磁盘,以便在应用程序退出并再次加载 View 时,消息被带回并通过标签显示。

目前,标签会显示用户输入的内容,但如果我退出应用并重新打开它,标签只会显示“您的消息”,这是我在 Storyboard 中分配给它的字符串。

我觉得这可能与 unarchiveObject(withFile:) 调用和/或对 String 的类型转换有关,因为我在某处读到自 Swift 以来3,值类型必须使用它们自己的方法(例如 unarchiveBool(withFile:))解档,尽管对于 String 似乎没有这样的方法。

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var outputLabel: UILabel!
    @IBOutlet weak var textBox: UITextField!

    var filePath: URL!

    override func viewDidLoad() {
        super.viewDidLoad()

        filePath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("message")

        // Load message
        if let loadedMessage = NSKeyedUnarchiver.unarchiveObject(withFile: filePath.absoluteString) as? String {
            outputLabel.text = loadedMessage
        }
    }

    @IBAction func textFieldDoneEditing(_ sender: UITextField) {
        sender.resignFirstResponder()
    }

    @IBAction func postButtonTapped(_ sender: Any) {
        outputLabel.text = textBox.text
        textBox.text = ""

        let str = outputLabel.text!

        // Save message      
        let data = NSKeyedArchiver.archivedData(withRootObject: str)
        do {
            try data.write(to: filePath)
        } catch {
            print("Couldn't write file.")
        }
    }
}

好的,@gkchristopher 已经指出 NSCoder 不能直接与 String 一起工作,所以我应该将我的数据包装在一个符合 NSCoding< 的类中。所以这是一个试图做到这一点的小类:

class DataThing: NSObject, NSCoding {
    var message: String!

    init(withMessage message: String) {
        self.message = message
    }

    required convenience init?(coder aDecoder: NSCoder) {
        self.init(withMessage: aDecoder.decodeObject(forKey: "message") as! String)
    }

    func encode(with aCoder: NSCoder) {
        aCoder.encode(message, forKey: "message")
    }
}

我可以使用这个类很好地写入数据:

let dataThing = DataThing(withMessage: str)

        // Save message
        let data = NSKeyedArchiver.archivedData(withRootObject: dataThing)
        do {
            try data.write(to: filePath)
        } catch {
            print("couldn't write file.")
        }

但是,读取还是不行:

if let loadedData = NSKeyedUnarchiver.unarchiveObject(withFile: filePath.absoluteString) as? DataThing {
            outputLabel.text = loadedData.message
}

我是否误解了“将数据包装在类中”?

最佳答案

更新的答案:

filePath.absoluteString 更改为 filePath.path

原答案:

NSCoder 可能无法直接使用 Swift 结构,例如 String。您需要将数据包装在一个类中并符合 NSCoding 才能使用 NSKeyedArchiver

例子:

let file = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("message")
let dataThing = DataThing(withMessage: "Boo")
NSKeyedArchiver.archiveRootObject(dataThing, toFile: file.path)

let retrieved = NSKeyedUnarchiver.unarchiveObject(withFile: file.path)

dump(retrieved)

关于ios - 为什么 NSKeyedArchiver 和 NSKeyedUnarchiver 在这里没有按预期工作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42051625/

相关文章:

objective-c - NSUSerDefaults 与 NSMutableArray 按 desc 顺序排序

c++ - Xcode 错误 - lipo : can't move temporary file: (is a directory)

arrays - 双方括号在swift中是什么意思?

swift - 如何实现 unarchiver 委托(delegate)来覆盖类名?

ios - iOS-编码/解码枚举-重新启动后在访问时崩溃

arrays - 如何使用 NSKeyedArchiver swift 将复杂的 Arrayobjects 保存到设备

ios - 无法将类型 '(User) -> Dictionary<String, String>' 的值转换为预期的参数类型 'Dictionary<String, String>'

ios - UITableview 的滑动手势

javascript - 我们如何处理 TVML 的逻辑以及它可以与 UIKit 一起使用吗?

ios - 如何刷新 UITableView 中的单节标题文本