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