简体   繁体   English

具有混合类型(可选和非可选)的Swift字典

[英]Swift dictionary with mix types (optional and non-optional)

I have a struct that has a method to return a dictionary representation. 我有一个结构,该结构具有返回字典表示形式的方法。 The member variables were a combination of different types (String and Double?) 成员变量是不同类型(字符串和双精度?)的组合

With the following code example, there would be a warning from Xcode (Expression implicitly coerced from 'Double?' to Any) 在下面的代码示例中,将出现来自Xcode的警告(表达式从'Double?'隐式强制为Any)

struct Record {
  let name: String
  let frequency: Double?

  init(name: String, frequency: Double?) {
    self.name = name
    self.frequency = frequency
  }

  func toDictionary() -> [String: Any] {
    return [
      "name": name,
      "frequency": frequency
    ]
  }
}

However if it was returning a type [String: Any?], the warning goes away: 但是,如果返回类型[String:Any?],则警告消失:

struct Record {
  let name: String
  let frequency: Double?

  init(name: String, frequency: Double?) {
    self.name = name
    self.frequency = frequency
  }

  func toDictionary() -> [String: Any?] {
    return [
      "name": name,
      "frequency": frequency
    ]
  }
}

My question is: Is this correct? 我的问题是:这正确吗? And if it is, can you point me to some Swift documentation that explains this? 如果是的话,您能指出一些解释这个问题的Swift文档吗?

If it isn't, what should it be? 如果不是,那应该是什么?

== EDIT == ==编辑==

The following works too: 以下工作也可以:

struct Record {
  let name: String
  let frequency: Double?

  init(name: String, frequency: Double?) {
    self.name = name
    self.frequency = frequency
  }

  func toDictionary() -> [String: Any] {
    return [
      "name": name,
      "frequency": frequency as Any
    ]
  }
}

You can cast frequency to Any since the latter can hold any type. 您可以将frequencyAny因为后者可以容纳任何类型。 It is like casting instances of specific Swift type to the Objective-C id type. 就像将特定Swift类型的实例转换为Objective-C id类型一样。 Eventually, you'll have to downcast objects of the type Any to a specific class to be able to call methods and access properties. 最终,您将必须将Any类型的对象向下转换到特定的类,以便能够调用方法和访问属性。

I would not recommend structuring data in your code using Any , or if you want to be specific Any? 我不建议您使用Any构造代码中的数据,或者如果您要特定于Any? (when the object may or may not hold some value). (当对象可能具有或不具有某些值时)。 That would be a sign of bad data-modeling. 那将是不良的数据建模的迹象。

From the documentation : 文档中

Any can represent an instance of any type at all, including function types.[...] Use Any and AnyObject only when you explicitly need the behavior and capabilities they provide. Any可以代表任何类型的实例,包括函数类型。[...] 当您明确需要它们提供的行为和功能时, 使用AnyAnyObject It is always better to be specific about the types you expect to work within your code. 最好对要在代码中使用的类型进行具体说明

(emphasis is mine) (重点是我的)

Instead, use the Data type. 而是使用Data类型。 And you would be able to decode Record or encode it from and into Data : 这样,您就可以对Record进行解码或将其编码为Data

struct Record : Codable {
    let name: String
    let frequency: Double?

    init(name: String, frequency: Double?) {
        self.name = name
        self.frequency = frequency
    }

    init(data: Data) throws { 
        self = try JSONDecoder().decode(Record.self, from: data) 
    }

    func toData() -> Data {
        guard let data = try? JSONEncoder().encode(self) else {
            fatalError("Could not encode Record into Data")
        }
        return data
    }
}

And use it like so: 并像这样使用它:

let record = Record(name: "Hello", frequency: 13.0)
let data = record.toData()

let decodedRecord = try Record(data: data)
print(decodedRecord.name)
print(decodedRecord.frequency ?? "No frequency")

I'd recommend adding Codable conformance, and letting JSONEncoder do all the heavy lifting. 我建议添加Codable一致性,并让JSONEncoder完成所有繁重的工作。 If however you are constrained to the toDictionary approach, then I would advise against [String:Any?] , since that might result in undefined behaviour (try to print the dictionary for more details). 但是,如果您受限于toDictionary方法,则建议不要使用[String:Any?] ,因为这可能会导致未定义的行为(尝试打印字典以获取更多详细信息)。

A possible solution for toDictionary is to use an array of tuples that gets converted to a dictionary: toDictionary的可能解决方案是使用元组数组,将其转换为字典:

func toDictionary() -> [String: Any] {
    let propsMap: [(String, Any?)] = [
        ("name", name),
        ("frequency", frequency)
    ]
    return propsMap.reduce(into: [String:Any]()) { $0[$1.0] = $1.1 }
}

This way the nil properties simply don't receive entries in the output dictionary. 这样,nil属性根本不会在输出字典中接收条目。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM