字典应该转换为 class 还是 Swift 中的结构?

Should a dictionary be converted to a class or struct in Swift?

我正在开发一个本机 iOS 应用程序,该应用程序从我们也可以控制的 Web 服务接收 JSON 格式的数据。计划是在大约 18 个月内更换后端数据库以支持不同的平台。

考虑到这一点,我们希望确保 iOS 应用能够相对容易地适应新的数据源,特别是因为我们可能会更改从服务器通过 JSON.

有两个目标:

  1. 为每个 PHP 请求创建一个位置,如果需要可以在其中修改密钥。这将避免挖掘代码以找到诸如 job["jobNumber"].

  2. 之类的东西
  3. 清理我们现有的代码以消除像 job["jobNumber"].

  4. 这样的引用

我们都是 Swift 的新手,没有 Objective-C 经验,但我认为 Struct 或 Class 适合创建像 job.jobNumber 这样的引用.

应该将字典转换为 class 还是结构?代表采用如下所示的 Dictionary<String, String> 并将其转换为推荐类型的可重用方法的示例代码将非常有帮助。

示例字典:

job = {
    "jobNumber" : "1234",
    "jobName" : "Awards Ceremony",
    "client" : "ACME Productions"
}

想要的结果:

println("job name is \(job.name)")
// prints: job name is Awards Ceremony

我一般都是利用值类来实现你想做的事情。在我的项目中,我做了如下操作:

protocol Request : class {
    func toDictionary() -> [String : String]
}

protocol Response : class {
    init(dictionary: [String : String])
}

class MyRequest : Request {
    var var1: Int
    var var2: String

    //Other stuff in class...

    func toDictionary() -> [String : String] {
        //Convert the value to dictionary and return
    }
}

class MyResponse : Response {
    var var1: String
    var var2: Int

    //You could have nested object as members 
    var innerObject: MyInnerResponseClass

    //Other stuff in class...

    var someCalculatedProperty: String {
        return //Calculate property value
    }

    required init(dictionary: [String : String]) {
        //Initialize class from dictionary
    }
}

class MyInnerResponseClass: Response {
    //Class definition here...
}

对于可用作请求和响应的对象,您可以实现这两种协议。

您只需要编写一次翻译代码,就可以在任何地方轻松使用。此外,通过使用计算属性,您可能会更加轻松。

我不确定您是否可以在 Swift 中开箱即用。我将需要 Swift 尚未很好支持的反思。另外,即使有反射并且你想出了聪明的方法来实现你需要的东西,如果数据很大,它可能会很慢。

要像这样访问它,您需要将字典转换为 Struct,如下所示:

edit/update: Swift 3.x

struct Job: CustomStringConvertible {
    let number: Int
    let name, client: String
    init(dictionary: [String: Any]) {
        self.number = dictionary["jobNumber"] as? Int ?? 0
        self.name = dictionary["jobName"] as? String ?? ""
        self.client = dictionary["client"] as? String ?? ""
    }
    var description: String {
        return "Job#: " + String(number) + " - name: " + name + " - client: " + client
    }
}

let dict: [String: Any] = ["jobNumber": 1234,
                                     "jobName"  : "Awards Ceremony",
                                     "client"   : "ACME Productions"]

let job = Job(dictionary: dict)
print(job.number)       //  1234
print(job.name)         //  "Awards Ceremony"
print(job.client)       //  "ACME Productions"
print(job)              // "Job#: 1234 - name: Awards Ceremony - client: ACME Productions"""

edit/update:

Swift 4 或更高版本 你可以使用 JSON Codable 协议:

struct Job {
    let number: Int
    let name, client: String
}
extension Job: Codable {
    init(dictionary: [String: Any]) throws {
        self = try JSONDecoder().decode(Job.self, from: JSONSerialization.data(withJSONObject: dictionary))
    }
    private enum CodingKeys: String, CodingKey {
        case number = "jobNumber", name = "jobName", client
    }
}
extension Job: CustomStringConvertible {
    var description: String {
        return "Job#: " + String(number) + " - name: " + name + " - client: " + client
    }
}

let dict: [String: Any] = ["jobNumber": 1234,
                           "jobName"  : "Awards Ceremony",
                           "client"   : "ACME Productions"]
do {
    let job = try Job(dictionary: dict)
    print(job.number)       //  1234
    print(job.name)         //  "Awards Ceremony"
    print(job.client)       //  "ACME Productions"
    print(job)              //  "Job#: 1234 - name: Awards Ceremony - client: ACME Productions\n"
} catch {
    print(error)
}

绝对是一个结构的工作。
1. 结构 thread-safe 不需要由 ARC 管理。
2. 一些研究发现,它们的工作速度通常比 类 快大约 30,000 倍。
3. Structs 还提供默认初始化程序,因此您的代码会更清晰。
4、这种情况下,你不用担心inheritance/subclassing.
5. 如果可以的话,面向协议的编程范例建议在 类 上使用结构。

struct Job {
    let number: Int
    let name: String
    let client: String
}

免费初始化器:

let newJob = Job(number: 2, name: "Honey", client: "Jeff")

或者您可以创建一个采用字典的自定义初始化程序:

struct Job {
    let number: Int
    let name: String
    let client: String

    init(json: [String: Any]) {
        self.number = Int(dictionary["jobNumber"] as? String) ?? 0
        self.name = dictionary["jobName"] as? String ?? ""
        self.client = dictionary["client"] as? String ?? ""
    }
}

用法:

let newJob = Job(json: yourDictionary)
print(newJob.number) 

// outputs: 1234

我的两分钱 "logic"。 )关于使用结构等的所有正确...)

不要将数据保存在字典或 JSON 中(就像很多来自网络的那样..),始终将其转换为结构。

效率很高,例如考虑在表视图中排序..

您可以像这样向字典添加扩展以获取通用对象:

extension Dictionary where Key == String, Value: Any {

    func object<T: Decodable>() -> T? {
        if let data = try? JSONSerialization.data(withJSONObject: self, options: []) {
            return try? JSONDecoder().decode(T.self, from: data)
        } else {
            return nil
        }
    }
}

并在任何 [String: Any] 词典上像这样使用:

let object: MyDecodableStruct? = dictionary.object()