【问题标题】:private(set) raises 'self' is immutable within structprivate(set) raises 'self' 在结构中是不可变的
【发布时间】:2018-02-13 05:39:50
【问题描述】:

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

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

(我不想将其设为计算属性并在 get {}, set{} 中执行此操作,因为在我的另一个项目中,我想根据不同于 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"

并建议“标记方法'mutating'以使'self'可变”。但这不是我想要的,因为我不想修改 Person 类型的实例,而是修改可变变量... (var favoriteNumber)

应该这样使用:

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

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

【问题讨论】:

  • mutating 正是您所需要的。它允许var 声明的变量更改它们的favoriteNumber 属性。
  • 是的,您需要var 才能使用mutating :)
  • @Cristik 感谢您的帮助并指出var 声明 - 我忽略了这一点:)

标签: ios swift access-control


【解决方案1】:

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

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

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

【讨论】:

    【解决方案2】:

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


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

    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 中的值。

    【讨论】:

      【解决方案3】:

      你不应该以你思考类的方式来思考结构。 swift 中的结构具有值语义。您不能更改使用 let 定义的结构的任何属性。即使结构内部的属性是用 var 定义的,因为 origin 是用 let 定义的。

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

      frank.favoriteNumber = 10
      

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

      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

      【讨论】:

        猜你喜欢
        • 2020-08-08
        • 1970-01-01
        • 2022-12-05
        • 1970-01-01
        • 1970-01-01
        • 2020-01-28
        • 1970-01-01
        • 1970-01-01
        • 2020-07-27
        相关资源
        最近更新 更多