如何复制结构并同时修改其属性之一?
How to copy a struct and modify one of its properties at the same time?
如果我想将我的视图控制器的状态表示为单个结构,然后实现撤消机制,我将如何更改结构上的一个 属性,同时获得一个先前状态的副本?
struct A {
let a: Int
let b: Int
init(a: Int = 2, b: Int = 3) {
self.a = a
self.b = b
}
}
let state = A()
现在我想要一个 state
的副本,但 b = 4。如何在不构造新对象并且不必为每个 属性 指定值的情况下执行此操作?
请注意,当您使用 常量 a
和 b
的占位符值时,您无法构造 A
的实例除此占位符之外的任何其他值。改为编写初始化程序。您也可以编写自定义方法来更改结构中的任何值:
struct A {
let a: Int
let b: Int
init(a: Int = 2, b: Int = 3) {
self.a = a
self.b = b
}
func changeValues(a: Int? = nil, b: Int? = nil) -> A {
return A(a: a ?? self.a, b: b ?? self.b)
}
}
let state = A()
let state2 = state.changeValues(b: 4)
如果您可以接受属性的可变性,这是另一种方法。优点是它适用于每个结构,并且在添加 属性:
时无需更改函数
struct A {
var a: Int
var b: Int
func changing(change: (inout A) -> Void) -> A {
var a = self
change(&a)
return a
}
}
let state = A(a: 2, b: 3)
let nextState = state.changing{ [=10=].b = 4 }
你也可以为此添加一个扩展:
protocol Changeable {}
extension Changeable {
func changing(change: (inout Self) -> Void) -> Self {
var a = self
change(&a)
return a
}
}
extension A : Changeable {}
您也可以使用它来完成它而无需任何额外的代码:
let nextState = {
var a = state
a.b = 4
return a
}()
如果你不介意新结构是可变的,它只是
var nextState = state
nextState.b = 4
我真的很喜欢@Shadow 的回答,但是我很难让它适应结构的字段可以为空的场景,所以我决定改用构建器模式。我的代码看起来像这样:
struct A {
let a: Int?
let b: Int?
class Builder {
private var a: Int?
private var b: Int?
init(struct: A) {
self.a = struct.a
self.b = struct.b
}
func build() -> A {
return A(a: self.a, b: self.b)
}
func withA(_ a: Int?) -> Builder {
self.a = a
return self
}
func withB(_ b: Int?) -> Builder {
self.b = b
return self
}
}
}
然后你可以像这样使用它:
A.Builder(struct: myA).withA(a).withB(nil).build()
有了这个我的结构真的是不可变的,我可以快速创建一个副本并将其一个或多个字段更改为 nil
或另一个 Int
我发现的最佳方法是编写一个初始化方法,该方法采用相同类型的对象进行“复制”,然后使用可选参数来设置要更改的每个 属性。
可选的 init
参数允许您跳过任何您希望与原始结构保持不变的 属性。
struct Model {
let a: String
let b: String
init(a: String, b: String) {
self.a = a
self.b = b
}
init(model: Model, a: String? = nil, b: String? = nil) {
self.a = a ?? model.a
self.b = b ?? model.b
}
}
let model1 = Model(a: "foo", b: "bar")
let model2 = Model(model: model1, b: "baz")
// Output:
// model1: {a:"foo", b:"bar"}
// model2: {a:"foo", b:"baz"}
这里的答案很荒谬,尤其是在结构的成员发生变化的情况下。
让我们了解 Swift 的工作原理。
当一个结构从一个变量设置为另一个变量时,该结构会自动克隆到新变量中,即相同的结构彼此不相关。
struct A {
let x: Int
var y: Int
}
let a = A(x: 5, y: 10)
var a1 = a
a1.y = 69
print("a.y = \(a.y); a1.y = \(a1.y)") // prints "a.y = 10; a1.y = 69"
请记住,如果您打算更改结构中的成员,则必须将它们标记为 var
,而不是 let
。
更多信息在这里:https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html
很好,但是如果您仍然想在一行中复制和修改,请将此函数添加到您的结构中:
func changing<T>(path: WritableKeyPath<A, T>, to value: T) -> A {
var clone = self
clone[keyPath: path] = value
return clone
}
现在把之前的例子改成这样:
let a = A(x: 5, y: 10)
let a1 = a.changing(path: \.y, to: 69)
print("a.y = \(a.y); a1.y = \(a1.y)") // prints "a.y = 10; a1.y = 69"
我看到将 'changing' 添加到很多结构中会很痛苦,但是扩展会很棒:
protocol Changeable {}
extension Changeable {
func changing<T>(path: WritableKeyPath<Self, T>, to value: T) -> Self {
var clone = self
clone[keyPath: path] = value
return clone
}
}
使用 'Changeable' 扩展您的结构,您将拥有 'changing' 功能。
对于 'changing' 函数方法,您在 'changing' 函数的调用站点(即 WritableKeyPath
类型)中指定的任何 属性 都应标记在原始结构为 var
,而不是 let
.
如果我想将我的视图控制器的状态表示为单个结构,然后实现撤消机制,我将如何更改结构上的一个 属性,同时获得一个先前状态的副本?
struct A {
let a: Int
let b: Int
init(a: Int = 2, b: Int = 3) {
self.a = a
self.b = b
}
}
let state = A()
现在我想要一个 state
的副本,但 b = 4。如何在不构造新对象并且不必为每个 属性 指定值的情况下执行此操作?
请注意,当您使用 常量 a
和 b
的占位符值时,您无法构造 A
的实例除此占位符之外的任何其他值。改为编写初始化程序。您也可以编写自定义方法来更改结构中的任何值:
struct A {
let a: Int
let b: Int
init(a: Int = 2, b: Int = 3) {
self.a = a
self.b = b
}
func changeValues(a: Int? = nil, b: Int? = nil) -> A {
return A(a: a ?? self.a, b: b ?? self.b)
}
}
let state = A()
let state2 = state.changeValues(b: 4)
如果您可以接受属性的可变性,这是另一种方法。优点是它适用于每个结构,并且在添加 属性:
时无需更改函数struct A {
var a: Int
var b: Int
func changing(change: (inout A) -> Void) -> A {
var a = self
change(&a)
return a
}
}
let state = A(a: 2, b: 3)
let nextState = state.changing{ [=10=].b = 4 }
你也可以为此添加一个扩展:
protocol Changeable {}
extension Changeable {
func changing(change: (inout Self) -> Void) -> Self {
var a = self
change(&a)
return a
}
}
extension A : Changeable {}
您也可以使用它来完成它而无需任何额外的代码:
let nextState = {
var a = state
a.b = 4
return a
}()
如果你不介意新结构是可变的,它只是
var nextState = state
nextState.b = 4
我真的很喜欢@Shadow 的回答,但是我很难让它适应结构的字段可以为空的场景,所以我决定改用构建器模式。我的代码看起来像这样:
struct A {
let a: Int?
let b: Int?
class Builder {
private var a: Int?
private var b: Int?
init(struct: A) {
self.a = struct.a
self.b = struct.b
}
func build() -> A {
return A(a: self.a, b: self.b)
}
func withA(_ a: Int?) -> Builder {
self.a = a
return self
}
func withB(_ b: Int?) -> Builder {
self.b = b
return self
}
}
}
然后你可以像这样使用它:
A.Builder(struct: myA).withA(a).withB(nil).build()
有了这个我的结构真的是不可变的,我可以快速创建一个副本并将其一个或多个字段更改为 nil
或另一个 Int
我发现的最佳方法是编写一个初始化方法,该方法采用相同类型的对象进行“复制”,然后使用可选参数来设置要更改的每个 属性。
可选的 init
参数允许您跳过任何您希望与原始结构保持不变的 属性。
struct Model {
let a: String
let b: String
init(a: String, b: String) {
self.a = a
self.b = b
}
init(model: Model, a: String? = nil, b: String? = nil) {
self.a = a ?? model.a
self.b = b ?? model.b
}
}
let model1 = Model(a: "foo", b: "bar")
let model2 = Model(model: model1, b: "baz")
// Output:
// model1: {a:"foo", b:"bar"}
// model2: {a:"foo", b:"baz"}
这里的答案很荒谬,尤其是在结构的成员发生变化的情况下。
让我们了解 Swift 的工作原理。
当一个结构从一个变量设置为另一个变量时,该结构会自动克隆到新变量中,即相同的结构彼此不相关。
struct A {
let x: Int
var y: Int
}
let a = A(x: 5, y: 10)
var a1 = a
a1.y = 69
print("a.y = \(a.y); a1.y = \(a1.y)") // prints "a.y = 10; a1.y = 69"
请记住,如果您打算更改结构中的成员,则必须将它们标记为 var
,而不是 let
。
更多信息在这里:https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html
很好,但是如果您仍然想在一行中复制和修改,请将此函数添加到您的结构中:
func changing<T>(path: WritableKeyPath<A, T>, to value: T) -> A {
var clone = self
clone[keyPath: path] = value
return clone
}
现在把之前的例子改成这样:
let a = A(x: 5, y: 10)
let a1 = a.changing(path: \.y, to: 69)
print("a.y = \(a.y); a1.y = \(a1.y)") // prints "a.y = 10; a1.y = 69"
我看到将 'changing' 添加到很多结构中会很痛苦,但是扩展会很棒:
protocol Changeable {}
extension Changeable {
func changing<T>(path: WritableKeyPath<Self, T>, to value: T) -> Self {
var clone = self
clone[keyPath: path] = value
return clone
}
}
使用 'Changeable' 扩展您的结构,您将拥有 'changing' 功能。
对于 'changing' 函数方法,您在 'changing' 函数的调用站点(即 WritableKeyPath
类型)中指定的任何 属性 都应标记在原始结构为 var
,而不是 let
.