2017-02-05 62 views
0

我一直在做一個項目,我需要寫一些對象到磁盤,然後再讓它們回來。我使用NSKeyedArchiverNSKeyedUnarchiver來完成寫作和閱讀,確保我的自定義類符合NSCoding。它沒有工作,所以,在嘗試調試它並搜索Web很長一段時間後,我決定隔離這個問題。爲什麼NSKeyedArchiver和NSKeyedUnarchiver在這裏按預期工作?

有人可以告訴我爲什麼這段代碼沒有做我期望它做的事嗎?這可能很明顯,但在盯着它好幾個小時後,我仍然無法解決它。

我得在Main.storyboardUILabel,一個UITextFieldUIButton定義的視圖控制器。這個想法是,用戶可以鍵入內容,按'發佈'按鈕,標籤將顯示他們輸入的內容。每次用戶輸入我希望它寫入磁盤的內容時,如果應用程序退出並再次加載視圖,則該消息將被帶回並由標籤顯示。

此刻,標籤顯示用戶輸入的內容,但如果我退出應用程序並重新打開該應用程序,標籤上只會顯示'Your message here',這是我在故事板中分配的字符串。

我有一種感覺,這可能是一些做的unarchiveObject(withFile:)呼叫和/或強制轉換爲String,因爲我讀的地方,因爲斯威夫特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 
} 

我誤解了'將數據包裝在課堂中'?

+0

你是否已經介入調試器? – Paulw11

+0

是的。它似乎正在成功寫入數據(沒有拋出異常),但是當我重建並運行應用程序時,'loadedMessage'字符串在本地變量查看器中寫有「無法讀取數據」。基於此,我認爲數據寫入方式存在問題(以至於以後無法讀取),或者讀取數據時出現問題。 – xander

+0

我現在已經將'filePath'設置爲'URL(fileURLWithPath:「/ Users/Xander/Desktop」).appendingPathComponent(「message」)'來看看如果我試圖直接寫入我的桌面會發生什麼。它工作,並且該文件似乎包含數據。仍然沒有解除它的存檔... – xander

回答

1

更新答案:

變化filePath.absoluteStringfilePath.path

原來的答覆:

NSCoder可以不與斯威夫特結構直接工作,如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) 
+0

這個類在評論中是非常不可讀的,但我不知道還有哪些地方可以發佈此添加... – xander

+0

@xander您可以編輯您的問題以添加更新的信息。 – Abizern

+0

@Abizern我知道,我只是覺得這樣做沒有意義......我現在可以。 – xander

0

你不需要創建自己的結構,你甚至都不需要一個存檔/取檔爲一個簡單的字符串。字符串可以直接讀取和寫入數據。

嘗試重寫你的代碼看起來像這樣。

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 = "" 
    } 

} 

我一直有點快和鬆散的錯誤,你可以把它們作爲具體到你的應用程序。

+0

我以爲是。所有子對象都符合'NSCoding',而不僅僅是根對象。在這種情況下,將符合類型('DataThing')的不合規類型('String')包裝起來似乎沒有幫助。 – xander

+0

對於簡單的結構,您可以創建一個字典表示並讀寫plists。對於任何更復雜的東西,還有其他形式的持久性可能更適合(核心數據,領域,地幔等) – Abizern

相關問題