Considérez la définition de classe suivante
class Class1 { var property: String { get { return "" } set { print("set called") } } }
Si vous ajoutez un point d'arrêt dans le bloc get et que vous lisez la propriété
, l'exécution est suspendue et vous observez que la méthode la plus élevée dans la pile d'appels est Class1.property.getter
De même, si vous ajoutez un point d'arrêt à l'intérieur du bloc d'ensemble et définissez la propriété
, l'exécution est suspendue et vous observez que la méthode la plus élevée dans la pile d'appels est Class1.property.setter
Lors du débogage d'un crash, j'ai observé que la méthode la plus élevée dans la pile d'appels était ClassName. computedPropertyName.modify
où ClassName
et computedPropertyName
sont des espaces réservés.
Quelqu'un peut-il indiquer ce que la méthode modifie
fait et quand il est appelé?
3 Réponses :
modify
est appelé chaque fois que vous modifiez la valeur de la propriété. Donc, pour une chaîne, ce serait à chaque fois que vous définissez, mettez à jour ou supprimez la valeur de ladite chaîne.
Ce qui suit serait un exemple de quand modify est appelé.
var string: String? //modify not called here string = ânew stringâ string = nil
get {} : lors de la récupération de la valeur d'une propriété, ce bloc de code sera exécuté.
set {} : lors de la définition de valeur d'une propriété cette partie du code sera exécutée.
Exemple
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) } }
Comme Vous pouvez en fait écrire des accesseurs Lors de la mutation de En termes simples, cela évite la copie de valeurs, ce qui peut déclencher des opérations de copie coûteuses pour les types de copie sur écriture tels que L'utilisation d'une coroutine permet de rendre temporairement une référence mutable à l'appelant, après quoi l'accesseur peut alors exécuter une logique supplémentaire. Par exemple: qui laisse d'abord l'appelant effectuer ses mutations, puis ajoute Le compilateur Swift peut synthétiser implicitement un code Le getter est d'abord appelé afin d'obtenir une copie mutable de la valeur, une référence à cette copie mutable est ensuite renvoyée à l'appelant, puis le setter est appelé avec la nouvelle valeur. L'accesseur Lors de la mutation de Je suppose que c'est parce que le compilateur a intégré l'implémentation de votre getter et setter dans le get
et set
, modify
est un accesseur. Cela fait partie de l'évolution vers les accesseurs généralisés a >, et est utilisé pour obtenir une référence mutable à une valeur sous-jacente à l'aide d'une coroutine yield-once. modify
dans Swift d'aujourd'hui en utilisant _modify
mot-clé. Cependant, notez que ce n'est pas encore une fonctionnalité officielle, donc tout code qui dépend explicitement de _modify
et yield
est sujet à rupture sans préavis. 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
, l'accesseur _modify
est appelé pour obtenir une référence mutable à un stockage sous-jacent. Le mot-clé yield
est utilisé pour transférer le contrôle vers l'appelant avec une référence au stockage de _property
. À ce stade, l'appelant peut appliquer des mutations arbitraires au stockage, dans ce cas en appelant + =
. Une fois la mutation terminée, le contrôle est retransféré vers _modify
, auquel point il revient. Pourquoi l'accesseur
modify
est-il utile? H3 > String
, Array
et Dictionnaire
(je parle de ce plus en détail ici a >). La mutation de c.property
via un accesseur modify
permet à la chaîne d'être mutée sur place, plutôt que de muter une copie temporaire qui est ensuite réécrite. Pourquoi
modify
utilise-t-il des coroutines? 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)
}
}
}
"world!"
à la fin de la chaîne. Pourquoi l'accesseur
modify
apparaît-il dans votre code? modify > accesseur pour les propriétés mutables. Pour une propriété calculée avec un getter et un setter, l'implémentation ressemble à ceci:
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!
modify
est principalement utilisé dans ce cas afin d'activer efficace mutation d'une propriété par envoi dynamique. Prenons l'exemple suivant: 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
, l'accesseur modify
est distribué dynamiquement à. S'il s'agit d'une instance de C
, cela permet à une référence au stockage de la propriété
d'être directement renvoyée à l'appelant, permettant une mutation sur place efficace. S'il s'agit d'une instance de D
, alors l'appel de modify
a juste le même effet que l'appel du getter suivi du setter. Pourquoi modifier apparaît comme l'appel le plus élevé dans la trace de pile de votre crash?
modifiez l'accesseur
, ce qui signifie que le plantage a probablement été causé par l'implémentation du getter ou du setter de votre propriété.
Obtenez-vous le plantage dans la
propriété
?Le code présent dans la question est uniquement à des fins d'illustration. J'ai observé un crash dans une application que j'ai écrite. Quand j'ai observé la pile d'appels au moment du crash, j'ai vu que le haut de la pile était la méthode
modify
d'une propriété calculée qui avait un getter et un setter