当 Swift 的结构体遇上 NSCoding

昨天在写一个 Model,需要用到 NSCoding 序列化,之前用 Class 是没问题的,但换到Struct 就歇菜了,NSCoding 并不支持 Swift 的结构体。

放 🐶 一搜,找到两种解决方法,这里总结一下:

方法一

为 Struct 增加一个 HelperClass 属性成员(Class),让后让它遵守 NSCoding 协议。接着为结构体添加编码 encode 和解码 decode 的类型方法,这对方法的内部其实是调用了 HelperClassdecodeObjectForKeyencodeObject 方法。

struct Person {  
  let firstName: String
  let lastName: String
}

extension Person {  
  class HelperClass: NSObject, NSCoding {

    var person: Person?

    init(person: Person) {
      self.person = person
      super.init()
    }

    class func path() -> String {
      let documentsPath = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true).first
      let path = documentsPath?.stringByAppendingString("/Person")
      return path!
    }

    required init?(coder aDecoder: NSCoder) {
      guard let firstName = aDecoder.decodeObjectForKey("firstName") as? String else { person = nil; super.init(); return nil }
      guard let laseName = aDecoder.decodeObjectForKey("lastName") as? String else { person = nil; super.init(); return nil }

      person = Person(firstName: firstName, lastName: laseName)

      super.init()
    }

    func encodeWithCoder(aCoder: NSCoder) {
      aCoder.encodeObject(person!.firstName, forKey: "firstName")
      aCoder.encodeObject(person!.lastName, forKey: "lastName")
    }
  }

使用起来也很简单:

let me = Person(firstName: "Dominik", lastName: "Hauser")

Person.encode(me)

let myClone = Person.decode()

firstNameLabel.text = myClone?.firstName  
lastNameLabel.text = myClone?.lastName  

方法二

定义一个 Protocol,包含两个方法:

  1. 从结构体中得到一个 NSDictionary 对象
  2. 使用一个 NSDictionary 对象初始化结构体

然后 NSDictionary 就可以使用 NSKeyedArchiver 进行序列化。这种方法的优点是:每个遵守该协议的结构体都可以被序列化,下面的代码将结构体归档到 NSUserDefaults 中。

// MARK: -
// MARK: Property list conversion protocol

protocol PropertyListReadable {  
    func propertyListRepresentation() -> NSDictionary
    init?(propertyListRepresentation:NSDictionary?)
}

func extractValuesFromPropertyListArray<T:PropertyListReadable>(propertyListArray:[AnyObject]?) -> [T] {  
    guard let encodedArray = propertyListArray else {return []}
    return encodedArray.map{$0 as? NSDictionary}
        .flatMap{T(propertyListRepresentation:$0)}
}

func saveValuesToDefaults<T:PropertyListReadable>(newValues:[T], key:String) {  
    let encodedValues = newValues.map{$0.propertyListRepresentation()}
    NSUserDefaults.standardUserDefaults()
        .setObject(encodedValues, forKey:key)
}

// MARK: -
// MARK: Coordinates

struct Coordinate {  
    let x:Double
    let y:Double
    let z:Double

    init(_ x: Double, _ y: Double, _ z: Double) {
        self.x = x
        self.y = y
        self.z = z
    }
}

extension Coordinate: PropertyListReadable {  
    func propertyListRepresentation() -> NSDictionary {
        let representation:[String:AnyObject] = ["x":self.x, "y":self.y, "z":self.z]
        return representation
    }

    init?(propertyListRepresentation:NSDictionary?) {
        guard let values = propertyListRepresentation else {return nil}
        if let xCoordinate = values["x"] as? Double,
            yCoordinate = values["y"] as? Double,
            zCoordinate = values["z"] as? Double {
                self.x = xCoordinate
                self.y = yCoordinate
                self.z = zCoordinate
        } else {
            return nil
        }
    }
}

-EOF-

如果感觉此文对你有帮助,请随意打赏支持作者 😘

chengway

认清生活真相之后依然热爱它!

Subscribe to Talk is cheap, Show me the world!

Get the latest posts delivered right to your inbox.

or subscribe via RSS with Feedly!