computed 属性 的 modify 方法何时被调用,它有什么作用?

When is the modify method of a computed property called and what does it do?

考虑以下 class 定义

class Class1 {
    var property: String {
        get {
            return ""
        }
        set {
            print("set called")
        }
    }
}

如果在 get 块内添加断点并读取 property,执行将暂停,您会观察到调用堆栈中最顶层的方法是 Class1.property.getter

同样,如果在 set 块内添加断点并设置 property,执行将暂停,您会观察到调用堆栈中最顶层的方法是 Class1.property.setter

在调试崩溃时,我观察到调用堆栈中最顶层的方法是 ClassName.computedPropertyName.modify,其中 ClassNamecomputedPropertyName 是占位符。

谁能指出 modify 方法的作用以及调用时间?

只要您更改 属性 的值,就会调用

modify。因此,对于一个字符串,您可以随时设置、更新或删除该字符串的值。

以下是调用修改的示例。

var string: String? //modify not called here 
string = “new string”
string = nil
  • 每次赋值时都会调用的代码
  • 计算属性始终是变量

get { } :当检索 属性 的值时,将执行此代码块。

set { } : 当设置 a 属性 的值时,这部分代码将被执行。

例子

var center: Point {
    get {
        let centerX = origin.x + (size.width / 2)
        let centerY = origin.y + (size.height / 2)
        return Point(x: centerX, y: centerY)
    }
    set(newCenter) {
        origin.x = newCenter.x - (size.width / 2)
        origin.y = newCenter.y - (size.height / 2)
    }
}

Swift Property Document

getset 一样,modify 是访问器。它是迈向 generalised accessors 的一部分,用于使用 yield-once 协程获取对基础值的可变引用。

您实际上可以使用 _modify 关键字在今天的 Swift 中编写 modify 访问器。但请注意,它还不是官方功能,因此任何明确依赖于 _modifyyield 的代码如有中断,恕不另行通知。

class C {
  var _property: String = ""
  var property: String {
    get {
      return _property
    }
    _modify {
      yield &_property
    }
  }
}

let c = C()
c.property += "hello"
print(c.property) // hello

在改变 c.property 后,调用 _modify 访问器以获得对某些底层存储的可变引用。 yield 关键字用于通过引用 _property 的存储将控制权转移回调用方。此时,调用者可以对存储应用任意突变,在本例中调用 +=。突变完成后,控制权将转移回 _modify,此时它 returns.

为什么 modify 访问器有用?

简单地说,它避免了值的复制,这会触发 copy-on-write 类型的昂贵复制操作,例如 StringArrayDictionary(我谈谈这个 )。通过 modify 访问器改变 c.property 允许改变字符串 in-place,而不是改变一个临时副本然后写回。

为什么 modify 使用协程?

协程的使用允许将可变引用暂时交还给调用者,之后访问者可以执行其他逻辑。

例如:

class C {
  var _property: String = ""
  var property: String {
    get {
      return _property
    }
    _modify {
      yield &_property
      _property += " world!"
    }
  }
}

let c = C()
c.property += "hello"
print(c.property) // hello world!

首先让调用者执行其突变,然后将 " world!" 附加到字符串的末尾。

为什么 modify 访问器出现在您的代码中?

Swift 编译器可以为可变属性隐式合成一个 modify 访问器。对于带有 getter 和 setter 的计算 属性,实现如下所示:

class Class1 {
  var property: String {
    get {
      return ""
    }
    set {
      print("set called")
    }
    // What the compiler synthesises:
    _modify {
      var tmp = property.get() // Made up syntax.
      yield &tmp
      property.set(tmp)
    }
  }
}

首先调用 getter 以获得值的可变副本,然后将对该可变副本的引用传递回调用者,然后调用 setter新值。

modify 访问器主要用于这种情况,以便通过动态调度实现 属性 的有效突变。考虑以下示例:

class C {
  var property = "hello" {
    // What the compiler synthesises:
    _modify {
      yield &property
    }
  }
}

class D : C {
  override var property: String {
    get { return "goodbye" }
    set { print(newValue) }
    // What the compiler synthesises:
    _modify {
      var tmp = property.get()
      yield &tmp
      property.set(tmp)
    }
  }
}

func mutateProperty(_ c: C) {
  c.property += "foo"
}

在改变 c.property 时,modify 访问器被动态调度到。如果这是 C 的实例,这允许对 property 的存储的引用直接返回给调用者,从而允许有效的 in-place 突变。如果这是 D 的一个实例,那么调用 modify 与调用 getter 后跟 setter.

的效果相同

为什么 modify 在崩溃的堆栈跟踪中显示为最顶层的调用?

我假设这是因为编译器已将 getter 和 setter 的实现内联到 modify 访问器中,因此这意味着崩溃可能是由实施 属性.

的 getter 或 setter