如何复制结构并同时修改其中一个属性?

Ian*_*ton 13 immutability swift

如果我想将我的视图控制器的状态表示为单个结构然后实现撤销机制,那么我如何更改结构上的一个属性,同时获取前一个状态的副本?

struct A {
   let a: Int
   let b: Int

   init(a: Int = 2, b: Int = 3) {
      self.a = a
      self.b = b
   }
}

let state = A()
Run Code Online (Sandbox Code Playgroud)

现在我想要一份state但是b = 4 的副本.如何在不构造新对象且必须为每个属性指定值的情况下执行此操作?

Sha*_* Of 20

请注意,当你使用占位符值常数 ab你是不是能够构建的情况下A与任何其他值,但这占位符.写初始化器.您可以编写自定义方法来更改struct中的任何值:

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)
Run Code Online (Sandbox Code Playgroud)

  • 遗憾的是,鉴于Swift善于复制价值观,你的答案中的逻辑不是语言的一部分. (7认同)

Kam*_*xom 5

如果您可以使用可变属性,那么这是另一种方法。优点是它适用于每个结构,并且无需在添加属性时更改函数:

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{ $0.b = 4 }
Run Code Online (Sandbox Code Playgroud)

您也可以对此进行扩展:

protocol Changeable {}
extension Changeable {
    func changing(change: (inout Self) -> Void) -> Self {
        var a = self
        change(&a)
        return a
    }
}

extension A : Changeable {}
Run Code Online (Sandbox Code Playgroud)

您也可以使用它来执行此操作,而无需任何其他代码:

let nextState = {
    var a = state
    a.b = 4
    return a
}()
Run Code Online (Sandbox Code Playgroud)

如果您不介意新结构是可变的,那只是

var nextState = state
nextState.b = 4
Run Code Online (Sandbox Code Playgroud)


Aru*_*yan 5

这里的答案很荒谬,尤其是在 struct 的成员发生变化的情况下。

让我们了解 Swift 是如何工作的。

当一个结构体从一个变量设置为另一个变量时,该结构体会自动克隆到新变量中,即相同的结构体彼此不相关。

struct A {
    let a: Int
    var b: Int
}

let a = A(a: 5, b: 10)
var a1 = a
a1.b = 69
print("a.b = \(a.b); a1.b = \(a1.b)") // prints "a.b = 10; a1.b = 69"
Run Code Online (Sandbox Code Playgroud)

请记住,如果您打算更改结构中的成员,则必须将其标记为“var”而不是“let”。

更多信息在这里:https : //docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html

这很好,但是如果您仍然想在一行中进行复制和修改,请将此函数添加到您的结构中:

func change<T>(path: WritableKeyPath<A, T>, to value: T) -> A {
    var clone = self
    clone[keyPath: path] = value
    return clone
}
Run Code Online (Sandbox Code Playgroud)

现在,之前的示例更改为:

let a = A(a: 5, b: 10)
let a1 = a.change(path: \.b, to: 69)
print("a.b = \(a.b); a1.b = \(a1.b)") // prints "a.b = 10; a1.b = 69"
Run Code Online (Sandbox Code Playgroud)

我看到向很多结构添加“更改”会很痛苦,但扩展会很棒:

protocol Changeable {}

extension Changeable {
    func change<T>(path: WritableKeyPath<Self, T>, to value: T) -> Self {
        var clone = self
        clone[keyPath: path] = value
        return clone
    }
}
Run Code Online (Sandbox Code Playgroud)

使用“Changeable”扩展您的结构,您将拥有“change”功能。

  • 我喜欢通过“WriteableKeyPath”进行扩展和设置,但这仍然需要将结构成员声明为可变的“var”...问题是(部分)试图避免的。 (6认同)
  • 答案称其他答案很荒谬,并表示如果您打算更改结构中的成员,则必须将其标记为 var,而不是 let。** (2认同)