private(set) raises 'self' 在结构中是不可变的

private(set) raises 'self' is immutable within struct

我不知道我是不是太累了,但我认为还有更多。 (试了两杯咖啡,还是没能解决问题。。。)

我想从外部将变量设置为只读,但通过方法可写。 (所以就像在那个例子中一样,我可以确保 favoriteNumber 只是个位数。

(我不想让它成为计算的 属性 并在 get {}、set{} 中执行此操作,因为在我的其他项目中我想根据不同于以下值的值修改变量"newValue" 个集合 {})

struct Person {

    var name: String
    private(set) var favoriteNumber: Int? // Should only be single-digit

    init(name: String, number: Int) {

        self.name = name

        // Make sure favorite number is single-digit
        guard number >= 0 && number < 10 else {
            self.favoriteNumber = nil
        }
        self.favoriteNumber = number
    }

    func changeFavoriteNumber(number: Int) {
        guard number >= 0 && number < 10 else { return }
        self.favoriteNumber = number
    }
}

.

self.favoriteNumber = number 

在函数中

changeFavoriteNumber(number:)

引发错误

"Cannot assign to property: 'self' is immutable"

并建议"Mark method 'mutating' to make 'self' mutable"。但这不是我想要的,因为我不想修改 Person 类型的实例,而是一个可变变量... (var favoriteNumber)

应该这样使用:

let frank = Person.init(name: "Frank", number: 9)
frank.changeFavoriteNumber(number: 8)

我不知道这里发生了什么(即使现在已经喝了 3 杯咖啡了:)

如果您不想使用 mutating,那么您必须将 Person 设为 class 而不是 struct。后者不允许突变,除非方法被标记为 mutable 并且实例是 var 变量而不是 let 变量。


为了完整起见,我还会提到使用 struct 解决此问题的方法,但我建议不要使用此解决方法。您可以 "box" 另一个(引用类型)对象中的 属性 值;这样你就可以掩盖突变。像这样:

class Box<T> {
  var value: T
  init(_ value: T) { self.value = value }
}

struct Person {
  private var favoriteNumber: Box<Int> = Box(0)

  func changeFavoriteNumber(_ number: Int) {
    self.favoriteNumber.value = number
  }
}

现在您可以使用 favoriteNumber.value.

访问 Person 中的值

结构是值类型,与 classes 具有不同的语义。存储 class 实例的变量被存储为对内存中其他地方的实际对象的引用;这意味着您可以有两个指向同一个对象的变量,并且您可以修改 class 的成员,无论引用它的变量是否可变。

结构不同。存储 struct 实例的变量直接存储结构的成员,而不是作为对其他地方对象的引用。这意味着将结构传递给函数或另一个变量会创建它的副本,而修改 struct 会直接修改存储它的变量。

因此,结构是不可变的,除非它存储在 var 中。必须按照编译器的建议声明改变结构的函数 mutating,以便编译器可以强制只在 let 结构上调用非改变函数。

您不应该像思考 类 那样思考结构。 swift 中的结构具有值语义。您不能更改使用 let 定义的结构的任何属性。尽管结构内部的属性是用 var 定义的,因为 origin 是用 let 定义的。

示例: 现在你可以这样做了:

frank.favoriteNumber = 10

但是如果你想改变一个结构的属性,因为它引用了值语义,你必须在你的函数中使用变异。变异是因为您正在改变结构的属性。 试试这个 变化:

mutating func changeFavoriteNumber(number: Int) {
        guard number >= 0 && number < 10 else { return }
        self.favoriteNumber = number
    }

var frank = Person.init(name: "Frank", number: 9)
frank.changeFavoriteNumber(number: 8)

来自苹果文档:

从实例方法中修改值类型 结构和枚举是值类型。默认情况下,不能从其实例方法中修改值类型的属性。 但是,如果您需要在特定方法中修改结构或枚举的属性,则可以选择对该方法的行为进行更改。然后该方法可以从方法内部改变(即更改)其属性,并且它所做的任何更改都会在方法结束时写回原始结构。该方法还可以为其隐式自身 属性 分配一个全新的实例,并且该新实例将在该方法结束时替换现有实例。 您可以通过在该方法的 func 关键字之前放置 mutating 关键字来选择加入此行为: URL