简体   繁体   English

应该将字典转换为 Swift 中的类或结构吗?

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

I am working on a native iOS application that receives data in JSON format from a web-service which we are also in control of.我正在开发一个本地 iOS 应用程序,该应用程序从我们也控制的 Web 服务接收 JSON 格式的数据。 The plan is to change out the backend database in a bout 18 months in favor of a different platform.计划是在大约 18 个月内更换后端数据库以支持不同的平台。

With that in mind, we want to be sure that that iOS app is going to be relatively easy to adapt to the new datasource, particularly as we may change the keys used in the associative array received from the server via JSON.考虑到这一点,我们希望确保 iOS 应用程序将相对容易适应新的数据源,特别是因为我们可能会更改通过 JSON 从服务器接收的关联数组中使用的键。

There are two goals:有两个目标:

  1. Create a single location for each PHP request where the keys can be modified if needed.为每个 PHP 请求创建一个位置,如果需要,可以在其中修改密钥。 This would avoid digging through code to find things like job["jobNumber"] .这将避免挖掘代码以查找诸如job["jobNumber"]

  2. Clean up our existing code to also eliminate references like job["jobNumber"] .清理我们现有的代码以消除像job["jobNumber"]这样的引用。

We are both very new to Swift with no Objective-C experience, but I am was thinking a Struct or Class would be appropriate to create references like job.jobNumber .我们都是 Swift 新手,没有 Objective-C 经验,但我认为 Struct 或 Class 适合创建像job.jobNumber这样的job.jobNumber

Should a dictionary be converted into a class or struct?应该将字典转换为类还是结构? Sample code representing a reusable method of taking a Dictionary<String, String> as shown below and converting it to the recommended type would be extremely helpful.表示采用Dictionary<String, String>的可重用方法(如下所示)并将其转换为推荐类型的示例代码将非常有帮助。

Example Dictionary:示例词典:

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

Desired result:想要的结果:

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

To access it like this you need to convert your dictionary to Struct as follow: 要像这样访问它,您需要将字典转换为Struct,如下所示:

edit/update: Swift 3.x 编辑/更新: 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 or later you can use JSON Codable protocol: 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)
}

I generally make use of value classes to achieve what you want to do. 我通常使用价值类来实现你想要做的事情。 In my project I do something like following: 在我的项目中,我做了类似以下的事情:

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...
}

For objects that could be used as request and response you could implement both of the protocols. 对于可以用作请求和响应的对象,您可以实现这两个协议。

You need to write code for translation once, but then it could be easy to use everywhere. 您需要编写一次翻译代码,但随后可以轻松地在任何地方使用。 Also by using calculated properties you may get extra ease. 此外,通过使用计算属性,您可以更轻松。

I am not sure if you could just do it out of the box in Swift. 我不确定你是否可以在Swift中开箱即用。 I will require reflection which is not yet very well supported by Swift. 我将需要Swift尚未得到很好支持的反思。 Also even if there is reflection and you come up with clever way to use to achieve what you need, it could be quite slow if the data is quite large. 即使有反射并且你想出了一个聪明的方法来实现你需要的东西,如果数据非常大,它可能会非常慢。

Definitely a job for a struct. 绝对是结构的工作。 Structs are thread safe and don't need to be managed by ARC. 结构是线程安全的,不需要由ARC管理。 Some studies have found them to be about 30,000x faster to work with than classes in general. 一些研究发现,与一般课程相比,它们的工作速度要快30,000倍。 Structs also provide default initializers so your code will be cleaner. 结构还提供默认初始值设定项,因此您的代码将更清晰。 Also in this case you don't have to worry about inheritance/subclassing. 同样在这种情况下,您不必担心继承/子类化。 You most likely also wouldn't want the dictionary to be treated as a reference type. 您很可能也不希望将字典视为引用类型。 The Protocol Oriented Programming paradigm recommends using structs over classes if you're able. 如果你能够,面向协议编程范例建议在类上使用结构。

struct Job {
    let jobNumber: Int
    let jobName: String
    let client: String
}

My two cents about "logic". 关于“逻辑”我的两分钱。 )all correct about using structs and so on...) )所有关于使用结构等的正确...)

Do NOT keep (as many from web do..) data in dict or JSON, convert it to struct always. 不要在dict或JSON中保留(尽可能多的来自web do ..)数据,始终将其转换为struct。

a lot of efficiently, think about for example about sorting in a tableview.. 很多有效的,想想例如关于在tableview中排序..

You can add an extension to Dictionary like this to get generic objects:您可以像这样向 Dictionary 添加扩展以获取通用对象:

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
        }
    }
}

and use like this on any [String: Any] dictionaries:并在任何[String: Any]字典上像这样使用:

let object: MyDecodableStruct? = dictionary.object()

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

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