【问题标题】:Read plist into specific type将 plist 读入特定类型
【发布时间】:2017-08-07 01:05:15
【问题描述】:

使用时:

let cities: [[String : String]] = {
    guard let URL = Bundle.main.url(forResource: "cities", withExtension: "plist") else {
        return []
    }
    return NSArray(contentsOf: URL) as! [[String : String]]
}()

从此:

<?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">
<array>
    <dict>
        <key>name</key>
        <string>Allentown, PA</string>
        <key>icao</key>
        <string>ABE</string>
    </dict>
    ....
</array>
</plist>

以后可以很容易地访问这些值:

    // inside tableView(_ cellForRowAt:)
    city = cities[indexPath.row]
    cell.icaoLabel?.text = city["icao"]
    cell.nameLabel?.text = city["name"]

但是,在声明时:

class City {
    var icao: String
    var name: String

    // is this init() necessary?
    init(icao: String, name: String) {
        self.icao = icao
        self.name = name
    }
}

和:

let cities: [City] = {
    guard let URL = Bundle.main.url(forResource: "cities", withExtension: "plist") else {
        return []
    }
    // ** No longer works
    return NSArray(contentsOf: URL) as! [City]
    // ** Doesn't work either
    return NSArray(contentsOf: URL)
}()

最终,我想使用:

    // inside tableView(_cellForRowAt:)
    city = cities[indexPath.row]
    cell icaoLabel?.text = city.icao
    cell nameLabel?.text = city.name

这是如何实现的?可以这样导入plist吗?

编辑到 City 类:

class City {
    var icao: String?
    var name: String?

    init(icao: String, name: String) {
        self.icao = icao
        self.name = name
    }

    static func loadCities() -> [City] {
        guard let URL = Bundle.main.url(forResource: "cities", withExtension: "plist") else {
            return []
        }
        let tempCities = NSArray(contentsOf: URL) as! [[String : String]]

        // now, create the desired cities array
        var cities = [City]()
        for city in tempCities {
            guard let icaoTemp = city["icao"], let nameTemp = city["name"] else {
                break
            }
            cities.append(City(icao: icaoTemp, name: nameTemp))
        }

        return cities
    }
}

访问值:

    let city: City = cities[indexPath.row]
    cell.icaoLabel?.text = city.icao
    cell.airportNameLabel?.text = city.name

【问题讨论】:

  • 不要用答案编辑您的问题。它否定了你问题的全部意义。

标签: arrays swift types properties


【解决方案1】:

[[String: String]] 是dictionary 数组的一种类型。但是,您的 City 类只是一个包含两个 String 对象的对象,它不是 dictionary 类型。所以你不能施放它。

let cities: [City] = {
    guard let URL = Bundle.main.url(forResource: "cities", withExtension: "plist")     else {
        return []
    }
    var cities = [City]()
    let myCites = NSArray(contentsOf: URL) as! [[String : String]]

    for city in myCites {
        cities.append(City(icao: city["icao"], name: city["name"]))
    }
    return cities
}()

【讨论】:

  • 这不会按原样编译,因为city["someKey"] 返回一个可选值,但City init 方法不接受可选值。
  • 这就是City的定义。如果city["someKey"] 是可选的,您应该定义someKey: String?
  • 更好的是,安全地解开字典中的值,因为City init 的参数不应该是可选的。
  • 尝试在这里学习。你说City 不是类型,它是Class。来自Apple docs on Value and Reference type:... The second, “reference types”, where instances share a single copy of the data, and the type is usually defined as a class.看到这里,你还说City不是类型吗?
  • 是的,它是一个refrence types,这意味着它引用了一个object。另外,我更新了上面的描述。
【解决方案2】:

不,没有内置方法可以将 plist 数据直接转换为 City 对象的数组。你需要自己做。

像现在一样加载 plist。然后迭代该数组以获取每个小字典。提取这两个值并使用您当前的init 创建一个City 实例,或者添加一个新的init,该init 接受一个字典并让City 类自行设置。

最后,您使用在迭代原始 plist 数组时创建的每个 City 实例构建一个新数组。

将所有这些功能包装到一个知道如何读取和写入 plist 文件并使用 City 实例列表的 Cities 类中可能是有意义的。您最终可能会在类中添加其他适当的逻辑来处理城市列表。

【讨论】:

  • 关于您在“更好,安全地展开...”上方的评论,请批评编辑(OP 底部的新内容)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多