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