Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/16.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios 为什么NSKeyedArchiver和NSKeyedUnachiver在这里不能按预期工作?_Ios_Swift_Nskeyedarchiver - Fatal编程技术网

Ios 为什么NSKeyedArchiver和NSKeyedUnachiver在这里不能按预期工作?

Ios 为什么NSKeyedArchiver和NSKeyedUnachiver在这里不能按预期工作?,ios,swift,nskeyedarchiver,Ios,Swift,Nskeyedarchiver,我一直在做一个项目,我需要把一些对象写到磁盘上,然后再把它们拿回来。我使用NSKeyedArchiver和NSKeyedUnarchiver进行编写和阅读,确保我的自定义类符合NSCoding。它不起作用,因此,经过长时间的调试和搜索,我决定隔离这个问题 有人能告诉我为什么这个代码没有达到我期望的效果吗?这可能是显而易见的,但在盯着它看了好几个小时后,我还是想不出来 我在Main.storyboard中定义了一个视图控制器,其中包含一个UILabel、一个UIExtField和一个UIButto

我一直在做一个项目,我需要把一些对象写到磁盘上,然后再把它们拿回来。我使用NSKeyedArchiver和NSKeyedUnarchiver进行编写和阅读,确保我的自定义类符合NSCoding。它不起作用,因此,经过长时间的调试和搜索,我决定隔离这个问题

有人能告诉我为什么这个代码没有达到我期望的效果吗?这可能是显而易见的,但在盯着它看了好几个小时后,我还是想不出来

我在Main.storyboard中定义了一个视图控制器,其中包含一个UILabel、一个UIExtField和一个UIButton。这个想法是用户可以输入一些内容,按下“Post”按钮,标签将显示他们输入的内容。每次用户输入我希望写入磁盘的内容时,如果退出应用程序并再次加载视图,则标签会返回并显示消息

此时,标签会显示用户输入的内容,但如果我退出应用程序并重新打开它,标签上只会显示“Your message here”,这是我在故事板中分配给它的字符串

我有一种感觉,这可能与unarchiveObjectwithFile:call和/或typecast to String有关,因为我在某个地方读到,自Swift 3以来,值类型必须通过使用自己的方法(例如unarchiveBoolwithFile:)来实现unarchiveObjectwithFile:,尽管String似乎没有这样的方法

好的,@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结构,例如字符串。为了使用NSKeyedArchiver,您需要将数据包装在一个类中并遵守NSCoding

例如:

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)

您不需要创建自己的结构,甚至不需要为简单的字符串创建Archiver/Unarchiver。字符串可以直接从数据中读取和写入

尝试重写代码,使其看起来像这样

class ViewController: UIViewController {

    @IBOutlet var label: UILabel!
    @IBOutlet var textField: UITextField!

    lazy var fileURL: URL = {
        do {
            var url = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
            url.appendPathComponent("message")
            return url
        } catch {
            fatalError("Cannot create storage file")
        }
    }()


    override func viewDidLoad() {
        super.viewDidLoad()
        do {
            let message = try String(contentsOf: fileURL)
            label.text = message

        } catch {
            label.text = "No message available"
        }
    }

    @IBAction func post(_ sender: UIButton) {
        label.text = textField.text

        guard
            let text = textField.text,
            let data = text.data(using: .utf8),
            text != ""
            else { return }

        do {
            try data.write(to: fileURL)
        } catch {
            print(error)
        }

        textField.text = ""
    }

}

我对错误的处理有点松懈,您可以根据应用程序的具体情况来处理它们。

调试程序是否已完成?是的。它似乎成功地写入了数据,没有引发异常,但当我重建并运行应用程序时,loadedMessage字符串在local variables viewer中写入了“无法读取数据”。基于此,我认为数据的写入方式存在问题,以至于以后无法读取,或者只是读取数据存在问题。我现在已将filePath设置为URLfileURLWithPath:/Users/Xander/Desktop。添加PathComponentMessage以查看如果直接写入桌面,会发生什么情况。它可以工作,并且文件似乎包含数据。但仍然无法取消归档…您实际上不需要创建自己的数据类型。直接读写数据就行了。这个类在评论中很难读,但我不知道还能在哪里发布这个添加…@xander你可以编辑你的问题来添加更新的信息。@Abizern我知道,我只是觉得这样做没有意义。。。不过我现在会的。另外,您应该尝试使用filePath.path而不是filePath.absoluteString。这两个在这种情况下是不同的。是的!这就是我刚才做的,它现在起作用了。虽然在做了这个更改之后,我不再需要使用我的DataThing类,我可以直接归档字符串。所有子对象都符合NSCoding,而不仅仅是根对象,这是有意义的。在这种情况下,将不一致的类型字符串包装在一致的类型DataThing中似乎没有什么帮助。对于简单的结构,您只需创建一个字典表示并对plists进行读写。对于任何更复杂的东西,还有其他形式的持久性可能更适合于核心数据、领域、地幔等
class ViewController: UIViewController {

    @IBOutlet var label: UILabel!
    @IBOutlet var textField: UITextField!

    lazy var fileURL: URL = {
        do {
            var url = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
            url.appendPathComponent("message")
            return url
        } catch {
            fatalError("Cannot create storage file")
        }
    }()


    override func viewDidLoad() {
        super.viewDidLoad()
        do {
            let message = try String(contentsOf: fileURL)
            label.text = message

        } catch {
            label.text = "No message available"
        }
    }

    @IBAction func post(_ sender: UIButton) {
        label.text = textField.text

        guard
            let text = textField.text,
            let data = text.data(using: .utf8),
            text != ""
            else { return }

        do {
            try data.write(to: fileURL)
        } catch {
            print(error)
        }

        textField.text = ""
    }

}