什么时候调用了计算属性的Modify方法,它的作用是什么?

时间:2019-06-03 11:46:52

标签: swift getter-setter

请考虑以下类定义

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方法的作用以及何时调用吗?

3 个答案:

答案 0 :(得分:2)

getset一样,modify是访问者。这是向generalised accessors迈进的一部分,用于通过一次产生一次的协程获取对基础值的可变引用。

实际上,您可以使用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,并在此时返回。

为什么modify访问器有用?

简而言之,它避免了值的复制,这会触发写入时复制类型(例如StringArrayDictionary)的昂贵复制操作(我在谈论in more detail here)。通过c.property访问器对modify进行突变可以使字符串就地进行突变,而不是对临时副本进行突变然后将其写回。

为什么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的存储的引用直接返回给调用方,从而实现有效的就地突变。如果这是D的实例,则调用modify的效果与调用getter和setter的效果相同。

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

我认为这是因为编译器已将getter和setter的实现内联到modify访问器中,因此,意味着崩溃很可能是由于属性的getter或setter的实现引起的

答案 1 :(得分:0)

每次更改属性值时都会调用

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

以下是调用修改的示例。

var string: String? //modify not called here 
string = “new string”
string = nil

答案 2 :(得分:0)

  • 每次分配代码时都会调用的代码段。
  • 计算属性始终是变量

获取{} :检索属性值时,将执行此代码块。

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

示例

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