Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/17.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Swift嵌套可选值类型(结构)和属性修改_Swift_Struct_Nested_Value Type - Fatal编程技术网

Swift嵌套可选值类型(结构)和属性修改

Swift嵌套可选值类型(结构)和属性修改,swift,struct,nested,value-type,Swift,Struct,Nested,Value Type,我在模型中使用了几种值类型,这种值类型(结构)具有嵌套其他值类型(结构)的属性。然后,在另一个结构中的一些嵌套结构中,使用我想要修改的根对象(添加、删除、更新)属性。此外,此属性通常具有可选类型,并且可以为零。因此,作为赋值给var的值类型,let得到复制,我不能使用这个内部结构实例的可选bing并在以后修改它们。因此,我进行此修改的唯一方法如下所示: if let cleaningDetails = initialPackage?.cleaningsSchedule?.details?[ind

我在模型中使用了几种值类型,这种值类型(结构)具有嵌套其他值类型(结构)的属性。然后,在另一个结构中的一些嵌套结构中,使用我想要修改的根对象(添加、删除、更新)属性。此外,此属性通常具有可选类型,并且可以为零。因此,作为赋值给var的值类型,let得到复制,我不能使用这个内部结构实例的可选bing并在以后修改它们。因此,我进行此修改的唯一方法如下所示:

if let cleaningDetails = initialPackage?.cleaningsSchedule?.details?[indexPath.row], cleaningDetails.startTimes == nil {
                        initialPackage?.cleaningsSchedule?.details?[indexPath.row].startTimes = []
                    }

因此,它实际上是使用值类型时的唯一选项。其他解决方案是什么?更改为类(引用类型)-这个函数型、值型编程真的那么棒吗?或者我应该在这个结构上使用更多的变异函数来方便修改吗?

您也可以为选项提供初始值,只要尝试声明数组,比如
var array:[Element]?=[]
这样您就不必检查nil值。您可以使用动态绑定来使用其他条件

首先,您应该减少系统中选项的数量。有多种方法可以处理可选集合(例如,像您建议的那样改变helper方法),但是可选的过度使用会造成许多不必要的复杂性。任何类型的集合都是可选的,这是非常罕见的。只有当
nil
和“empty”的意思不同时(这是非常罕见的),这才有意义

与其将整个数据模型包装在特定的JSON API上,不如将JSON转换为所需的数据模型。例如,这里有一个JSON模型,它包含一个必需的Int,可能包含也可能不包含数组,但在内部,我们希望将“缺少的数组”视为“空”。我们还希望在发送空数组之前去掉空数组

import Foundation

let json = Data("""
{
    "y": 1
}
""".utf8)

struct X {
    var y: Int
    var z: [String]
}

extension X: Codable {
    enum CodingKeys: String, CodingKey {
        case y, z
    }
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        y = try container.decode(Int.self, forKey: .y)
        z = try container.decodeIfPresent([String].self, forKey: .z) ?? []
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(y, forKey: .y)
        if !z.isEmpty {
            try container.encode(z, forKey: .z)
        }
    }
}

let decoder = JSONDecoder()
print(try decoder.decode(X.self, from: json))

let encoder = JSONEncoder()
print(String(data: try encoder.encode(X(y: 1, z: [])), encoding: .utf8)!)
这会将所有工作转移到两种方法中,而不是在每次访问数据模型时将其分散到整个程序中。自定义代码表的编写仍然有点乏味(并且可能会在编码器步骤中引入一些微妙的错误),因此如果您有很多自定义代码表,您应该看看哪些代码表可以为您编写

如果您真的想跟踪某个键是否丢失或为空(因此您可能会按照发送给您的方式重新编码),那么我可能会通过以下方式跟踪可选属性:

struct X: Codable{
    enum CodingKeys: String, CodingKey {
        case y
        case _z = "z"
    }

    var y: Int
    private var _z: [String]?  // The actual `z` we got from the JSON
    var z: [String] { get { return _z ?? [] } set { _z = newValue } }

    init(y: Int, z: [String]?) {
        self.y = y
        self._z = z
    }
}
“real”
z
存储在
\u z
中,可以重新序列化,但程序的其余部分从未看到可选的

另一种比较常见的技术是创建一个适配器层,该层将“JSON兼容”结构转换为内部数据模型并返回。如果方便的话,这允许您的内部数据模型与JSON略有不同


当然,您也可以创建helper方法,但所有这些的真正关键是不允许Optionals泄漏到程序的其他部分,而这些部分实际上不是可选的。如果系统中某个地方一定存在复杂性,请将其放在解析/编码的位置,而不是使用的位置。

现在还不清楚您在问什么,我怀疑您过度使用了选项。使用可选数组(或其他集合,包括字符串)是非常不明智的。只需将默认值设置为
[]
而不是
nil
,然后去掉选项。将
nil
替换为
[]
的事实表明
nil
[]
实际上是一回事。(只要这是真的,就不应该使用可选的。)此数组中,字符串被定义为从web api响应解码的model struct的属性,并且很多时候此字段可能不可用。在使用JSONDecoder时,选项是使此模型具有灵活性的唯一方法。我尝试修改这些对象,然后将它们重新发送到web api。对我来说,这种可选的链接似乎有点麻烦,我想知道这是否是修改此类对象的唯一解决方案。
JSONDecoder
可以避免这种情况。您可以实现一个
init(from:)
方法,该方法分配空值并且不需要选项,您还可以实现一个
encode
方法,如果您也需要,该方法可以删除空值。不应允许JSON兼容性问题在整个数据模型中传播。这使得它比需要的要复杂得多(正如你所发现的)