2015-10-16 37 views
2

Swift中應該如何處理與NSCoding相關的錯誤?處理Swift中的NSCoding錯誤

當使用init?(coder:)對對象進行初始化時,如果數據無效,則可能無法初始化對象。我想抓住這些錯誤並妥善處理它們。爲什麼init?(coder:)沒有在Swift中定義爲拋出函數?

回答

5

NSCoding將其定義爲可選:

init?(coder aDecoder: NSCoder) 

所以它肯定是可以檢測的錯誤。

90%的「爲什麼Swift ...?」問題可以用「因爲可可」來解答。可可並沒有將initWithCoder:定義爲返回一個錯誤,所以它不會在Swift中轉換爲throws。沒有辦法將它與現有代碼完美地結合在一起。 (NSCoding追溯到NeXTSTEP的,因爲我們已經有很多軟件不返回NSError那裏。這並不意味着它可能不是很好的時候,但是「無法初始化」已經夠傳統)

檢查nil。這意味着某些失敗。這是提供的所有信息。


我從來沒有在實踐中必須檢查太深,整個對象圖是正確的。如果不是,那麼你很可能會得到其他錯誤,並且記住NSKeyedUnarchiver如果解碼失敗將會引發一個ObjC異常(!!!)。除非你將它包裝在ObjC @catch中,否則無論如何你都會崩潰。 (是的,這是非常瘋狂的,但仍然如此。)

但是,如果我想非常小心,並確保我期望在檔案中的東西真的在檔案中(即使它們是nil),我可能會做這種方式(未經測試,它編譯,但我還沒有確定它確實有效):

import Foundation 

enum DecodeError: ErrorType { 
    case MissingProperty(String) 
    case MalformedProperty(String) 
} 

extension NSCoder { 
    func encodeOptionalObject(obj: AnyObject?, forKey key: String) { 
     let data = obj.map{ NSKeyedArchiver.archivedDataWithRootObject($0) } ?? NSData() 
     self.encodeObject(data, forKey: key) 
    } 

    func decodeRequiredOptionalObjectForKey(key: String) throws -> AnyObject? { 
     guard let any = self.decodeObjectForKey(key) else { 
      throw DecodeError.MissingProperty(key) 
     } 

     guard let data = any as? NSData else { 
      throw DecodeError.MalformedProperty(key) 
     } 

     if data.length == 0 { 
      return nil // Found nil 
     } 

     // But remember, this will raise an ObjC exception if it's malformed data! 
     guard let prop = NSKeyedUnarchiver.unarchiveObjectWithData(data) else { 
      throw DecodeError.MalformedProperty(key) 
     } 

     return prop 
    } 
} 

class MyClass: NSObject, NSCoding { 
    static let propertyKey = "property" 
    let property: String? 
    init(property: String?) { 
     self.property = property 
    } 
    required init?(coder aDecoder: NSCoder) { 
     do { 
      property = try aDecoder.decodeRequiredOptionalObjectForKey(MyClass.propertyKey) as? String 
     } catch { 
      // do something with error if you want 
      property = nil 
      super.init() 
      return nil 
     } 
     super.init() 
    } 

    func encodeWithCoder(aCoder: NSCoder) { 
     aCoder.encodeOptionalObject(property, forKey: MyClass.propertyKey) 
    } 
} 

正如我所說的,在可可節目,我從來沒有真正做到了這一點。如果存檔中的任何內容真的損壞了,那麼幾乎肯定會結束引發ObjC異常的情況,所以所有這些錯誤檢查都可能是過度的。但它確實可以讓你區分「無」和「不在檔案中」。我只是編碼屬性單獨作爲NSData,然後編碼NSData。如果它是nil,我編碼一個空的NSData

+0

這很有道理。你的觀點是,它建立在現有的可可行爲上是完全有效的。我認爲這可能是我對「NSCoding」理解的一個問題。在Objective-C中做類似的事情我已經在try/catch塊中包裝了一些複雜圖形的頂級對象,但是這裏的錯誤實際上必須是我將nil值分配給非可選屬性,在初始化時解碼如果任何值爲零,整個初始化器應該返回nil。這聽起來正確嗎? –

+0

正確。你需要返回nil(這也意味着,今天不幸)你必須找到* some *的方式來初始化所有的屬性。你必須在返回nil之前初始化所有的東西,而Swift團隊承認的東西有點壞。 –

+0

明白了。我還沒有消化failable initializers,但我可以學習。你有沒有處理有效的零值的建議?例如:在類型A的存檔對象中有一個類型爲B的可選屬性。如果類型B的實例不能被初始化,它的'init?(coder:)'返回nil。對象A將使用'nil'初始化它的屬性並處於有效狀態。我寧願讓對象A仍然失敗,因爲如果圖中的任何屬性都失敗了,它就沒有意義了。那有意義嗎?在這種情況下有沒有辦法處理? –