Как get
и set
, modify
- это средство доступа. Это часть движения к обобщенным методам доступа , и используется для получения изменяемой ссылки на базовое значение с использованием сопрограммы с однократным выходом.
Вы можете написать modify
аксессоров в современном Swift, используя ключевое слово _modify
. Однако обратите внимание, что это еще не официальная функция, поэтому любой код, который явно зависит от _modify
и yield
, может быть взломан без предварительного уведомления.
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
?
Проще говоря, это позволяет избежать копирования значений, что может вызвать дорогостоящие операции копирования для типов копирования при записи, таких как String
, Array
и Dictionary
(я говорю об этом более подробно подробно здесь ). Мутирование 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
для изменяемых свойств. Для вычисляемого свойства с геттером и сеттером реализация выглядит следующим образом:
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)
}
}
}
Сначала вызывается метод получения, чтобы получить изменяемую копию значения, затем ссылка на эту изменяемую копию передается вызывающей стороне, а затем вызывается установщик с новым значением.
Аксессор 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
имеет тот же эффект, что и вызов метода получения с последующим установщиком.
Почему modify
отображается как самый верхний вызов в трассировке стека вашего сбоя?
Я бы предположил, что это потому, что компилятор встроил реализацию вашего метода получения и установки в аксессор modify
, что означает, что сбой, вероятно, был вызван реализацией либо метода получения, либо установки вашего свойства.