private(set)引发'self'在struct

时间:2018-02-12 19:46:29

标签: ios swift access-control

我不知道我是否太累了,但我认为还有更多。 (我尝试了两杯咖啡,但仍然无法解决问题......)

我想从外部创建一个只读变量,但可以通过方法写入。 (所以在那个例子中我可以确保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方法'改变'以使'自我'可变”。但这不是我想要的,因为我不想修改Person类型的实例,而是一个可变变量...( var favoriteNumber)

假设以这种方式使用:

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

我不知道这里发生了什么(即使现在有3个咖啡:)

3 个答案:

答案 0 :(得分:3)

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

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

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
  }
}

现在,您可以使用Person访问favoriteNumber.value内的值。

答案 1 :(得分:3)

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

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

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

答案 2 :(得分:1)

你不应该以与思考类相同的方式来思考结构。 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)

来自苹果文档:

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