Когда вызывается метод изменения вычисляемого свойства и что он делает? - PullRequest
2 голосов
/ 03 июня 2019

Рассмотрим следующее определение класса

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

Если вы добавите точку останова внутри блока get и прочитаете property, выполнение будет приостановлено, и вы увидите, что самый верхний метод в стеке вызовов равен Class1.property.getter

Аналогичным образом, если вы добавите точку останова в блоке установки и установите property, выполнение будет приостановлено, и вы увидите, что самый верхний метод в стеке вызовов равен Class1.property.setter

Во время отладкиВ результате сбоя я заметил, что самый верхний метод в стеке вызовов был ClassName.computedPropertyName.modify, где ClassName и computedPropertyName являются заполнителями.

Кто-нибудь может указать, что делает метод modify и когда он вызывается?

Ответы [ 3 ]

2 голосов
/ 03 июня 2019

Как 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, что означает, что сбой, вероятно, был вызван реализацией либо метода получения, либо установки вашего свойства.

0 голосов
/ 03 июня 2019
  • фрагментов кода, которые будут вызываться каждый раз при их назначении.
  • Вычисляемые свойства всегда являются переменными

get {} : когдапри получении значения свойства будет выполнен этот блок кода.

set {} : при установке значения свойства будет выполнена эта часть кода.

Пример

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

0 голосов
/ 03 июня 2019

modify вызывается каждый раз, когда вы меняете значение свойства.Таким образом, для строки это будет каждый раз, когда вы устанавливаете, обновляете или удаляете значение указанной строки.

Ниже приведен пример, когда вызывается изменение.

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