【问题标题】:Swift: pList with "complex" dataSwift:具有“复杂”数据的 pList
【发布时间】:2016-05-18 13:06:28
【问题描述】:

当我阅读和尝试时 :-) 我只能在 pList 文件中保存一些简单的数据类型。尽管如此,我还是喜欢使用结构、类等来表示我的数据。这应该尽可能容易地保存到 pList 文件并重新加载。

我明白了,NSData 是 pLists 的有效类型。而且,这是一种通用数据类型。因此,将结构或类变量移动/转换/强制到要保存和重新加载的 NSData 对象中是个好主意吗?那要怎么做呢?

到目前为止,我正在使用类似的东西来保存:

let dict: NSMutableDictionary = ["XYZ": "XYZ"]

// saving values
dict.setObject(myBasicArray, forKey: "BasicArray")

dict.writeToFile(path, atomically: false)

更新: 我使用了提供的代码并将其扩展为处理结构:

import Cocoa

struct SteeringItem {
    var ext    = String()   // extension including dot e.g. ".JPG"
    var bitmap = Int()      // flag for grouping file types, e.g. photos

    init?(ext: String, bitmap: Int) {
        // Initialize stored properties.
        self.ext    = ext
        self.bitmap = bitmap
    }
}

class Foo {
    let one: Int
    let two: SteeringItem


    init(one:Int, two: SteeringItem) {
        self.one = one
        self.two = two
    }

    init?(dict:[String: AnyObject]) {
        guard let
            one = dict["one"] as? Int,
            two = dict["two"] as? SteeringItem else { return nil }

        self.one = one
        self.two = two
    }

    func toDictionary() -> [String: AnyObject] {
        var retval = [String: AnyObject]()
        if let
            one = self.one as? AnyObject,
            two = self.two as? AnyObject {
            retval["one"] = one
            retval["two"] = two
        }
        return retval
    }
}

// create struct
let writeStruct = Foo(one: 1, two: SteeringItem(ext: "one",bitmap: 1)!)
print(writeStruct, "\n")

// write to plist
let writeDict = writeStruct.toDictionary() as NSDictionary
let path = ("~/test.plist" as NSString).stringByExpandingTildeInPath
writeDict.writeToFile(path, atomically: true)

// print contents of file
print(try NSString(contentsOfFile: path, encoding: NSUTF8StringEncoding))

// read plist and recreate struct
if let
    readDict = NSDictionary(contentsOfFile: path) as? [String:AnyObject],
    readStruct = Foo(dict: readDict) {
    print(readStruct)
}

但这不再写了。使用 String 可以,使用“struct SteeringItem”则不行!

更新 2:类而不是结构

    class SteeringItem : NSObject, NSCoding {
    var ext    = String()   // extension including dot e.g. ".JPG"
    var bitmap = Int()      // flag for grouping file types, e.g. photos

    func encodeWithCoder(aCoder: NSCoder) {
        aCoder.encodeObject(ext,    forKey: "ext")
        aCoder.encodeObject(bitmap, forKey: "bitmap")
    }

    required convenience init?(coder aDecoder: NSCoder) {
        let ext    = aDecoder.decodeObjectForKey("ext")    as! String
        let bitmap = aDecoder.decodeObjectForKey("bitmap") as! Int

        self.init(ext: ext, bitmap: bitmap)
    }

    init?(ext: String, bitmap: Int) {
        // Initialize stored properties.
        self.ext    = ext
        self.bitmap = bitmap

        super.init()
    }
}

【问题讨论】:

  • 根据您数据的复杂性,可能值得检查一下CoreData,看看它是否更合适。
  • 如果init 不会失败,你就不需要它。就此而言,如果 struct 没有定义任何 init 方法,则它有一个默认的 init 进行成员初始化。因此,您的 SteeringItem struct 中不需要 init 方法。

标签: swift save plist nsdata


【解决方案1】:

有几种方法可以做到这一点,您可以遵守NSCoding 协议,或者您可以编写方法将您的类/结构转换为字典并从那里序列化。

这里是使用 NSCoding protocol 的一个很好的介绍。

至于与 Dictionary 之间的转换,通常的方法是提供一个可失败的 init 方法,该方法采用 Dictionary<String, AnyObject> 验证键:值对并将其复制到成员变量。您还提供了一个返回 Dictionary<String, AnyObject> 的方法,该方法具有与 init 相同的键:值对。然后,您可以通过调用 create 方法并序列化生成的 Dictionary 来进行序列化,您可以通过读入 Dictionary 并将其传递给 init 方法来反序列化。

以下是转换示例:

/// Provides conversion to and from [String: AnyObject] for use in serialization
protocol Serializable {
  init?(dict:[String: AnyObject])
  func toDictionary() -> [String: AnyObject]
}

struct SteeringItem {
  // Changed var to let, it's a good practice with a simple struct
  let ext    : String   // extension including dot e.g. ".JPG"
  let bitmap : Int      // flag for grouping file types, e.g. photos
}

struct Foo {
  let one: Int
  let two: SteeringItem
}

// Add serialization to structs

extension SteeringItem: Serializable {
  init?(dict:[String: AnyObject]) {
    guard let
      ext = dict["ext"] as? String,
      bitmap = dict["bitmap"] as? Int else { return nil }
    self.ext = ext
    self.bitmap = bitmap
  }

  func toDictionary() -> [String: AnyObject] {
    var retval = [String: AnyObject]()
    if let
      ext = self.ext as? AnyObject,
      bitmap = self.bitmap as? AnyObject {
      retval["ext"] = ext
      retval["bitmap"] = bitmap
    }
    return retval
  }
}

extension Foo: Serializable {
  init?(dict:[String: AnyObject]) {
    guard let
      one = dict["one"] as? Int,
      twoDict = dict["two"] as? [String: AnyObject],
      two = SteeringItem(dict: twoDict) else { return nil }

    self.one = one
    self.two = two
  }

  func toDictionary() -> [String: AnyObject] {
    var retval = [String: AnyObject]()
    let twoDict = self.two.toDictionary()
    if let
      one = self.one as? AnyObject,
      two = twoDict as? AnyObject {
      retval["one"] = one
      retval["two"] = two
    }
    return retval
  }
}

这是测试它的方法(在操场上):

import Foundation

// create struct
let writeStruct = Foo(one: 1, two: SteeringItem(ext: "jpg", bitmap: 1))
print(writeStruct, "\n")

// write to plist
let writeDict = writeStruct.toDictionary() as NSDictionary
let path = ("~/test.plist" as NSString).stringByExpandingTildeInPath
writeDict.writeToFile(path, atomically: true)

// print contents of file
print(try NSString(contentsOfFile: path, encoding: NSUTF8StringEncoding))

// read plist and recreate struct
if let
  readDict = NSDictionary(contentsOfFile: path) as? [String:AnyObject],
  readStruct = Foo(dict: readDict) {
  print(readStruct)
}

结果:

Foo(one: 1, two: SteeringItem(ext: "jpg", bitmap: 1)) 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>one</key>
    <integer>1</integer>
    <key>two</key>
    <dict>
        <key>bitmap</key>
        <integer>1</integer>
        <key>ext</key>
        <string>jpg</string>
    </dict>
</dict>
</plist>

Foo(one: 1, two: SteeringItem(ext: "jpg", bitmap: 1))

【讨论】:

  • 哇!我学到了很多。现在我可以拯救我的班级。接下来我会检查,如果我可以重新加载它。 :-) 非常感谢!
  • 一个问题:我如何让课堂回来?通常我使用 "if dict.objectForKey("myVar") != nil { myVar = d.objectForKey("myVar")! as! [String] }" 但这失败了。应该是这样的演员表:“myVar = MyClass(dict: dict as![String: AnyObject])!” ?
  • 我用Dictionary读写的例子更新了答案
  • 我添加了我使用的代码,我可以开始工作了。
  • 您必须序列化每个要存储到 plist 中的成员变量。这样做的方法是创建一个需要两个序列化函数的协议,然后让你的类采用它。当您想存储成员变量时,您调用该方法并将其存储在要序列化的Dictionary
猜你喜欢
  • 2015-05-27
  • 2011-01-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-09
  • 1970-01-01
  • 1970-01-01
  • 2021-06-12
相关资源
最近更新 更多