documentation d'Apple précisez que:
les observateurs willSet et didSet ne sont pas appelés lors de la première initialisation d'une propriété. Ils ne sont appelés que lorsque la valeur de la propriété est définie en dehors d’un contexte d’initialisation.
Est-il possible de forcer ces derniers à être appelés lors de l'initialisation?
Disons que j'ai ce cours
class SomeClass {
var someProperty: AnyObject {
didSet {
doStuff()
}
}
init(someProperty: AnyObject) {
self.someProperty = someProperty
doStuff()
}
func doStuff() {
// do stuff now that someProperty is set
}
}
J'ai créé la méthode doStuff
, pour rendre les appels de traitement plus concis, mais je préfère traiter la propriété dans la fonction didSet
. Existe-t-il un moyen de forcer cet appel lors de l'initialisation?
J'ai décidé de simplement supprimer l'initialisateur de commodité pour ma classe et de vous forcer à définir la propriété après l'initialisation. Cela me permet de savoir que didSet
sera toujours appelé. Je n'ai pas encore décidé si cela est meilleur dans l'ensemble, mais cela convient bien à ma situation.
Créez votre propre méthode set et utilisez-la dans votre méthode init:
class SomeClass {
var someProperty: AnyObject! {
didSet {
//do some Stuff
}
}
init(someProperty: AnyObject) {
setSomeProperty(someProperty)
}
func setSomeProperty(newValue:AnyObject) {
self.someProperty = newValue
}
}
Si vous utilisez defer
à l'intérieur d'un initialiseur, pour mettre à jour les propriétés facultatives ou pour mettre à jour les propriétés non facultatives que vous avez déjà initialisées et après que vous ayez appelé super.init()
méthodes, puis votre willSet
, didSet
, etc. sera appelée. Je trouve cela plus pratique que de mettre en œuvre des méthodes distinctes qui vous obligent à garder la trace des appels aux bons endroits.
Par exemple:
public class MyNewType: NSObject {
public var myRequiredField:Int
public var myOptionalField:Float? {
willSet {
if let newValue = newValue {
print("I'm going to change to \(newValue)")
}
}
didSet {
if let myOptionalField = self.myOptionalField {
print("Now I'm \(myOptionalField)")
}
}
}
override public init() {
self.myRequiredField = 1
super.init()
// Non-defered
self.myOptionalField = 6.28
// Defered
defer {
self.myOptionalField = 3.14
}
}
}
Rendra:
I'm going to change to 3.14
Now I'm 3.14
En variante de la réponse d'Oliver, vous pouvez envelopper les lignes dans une fermeture. Par exemple:
class Classy {
var foo: Int! { didSet { doStuff() } }
init( foo: Int ) {
// closure invokes didSet
({ self.foo = foo })()
}
}
Edit: la réponse de Brian Westphal est plus gentille à mon humble avis. La bonne chose à propos de son, c'est qu'il fait allusion à l'intention.
J'ai eu le même problème et cela fonctionne pour moi
class SomeClass {
var someProperty: AnyObject {
didSet {
doStuff()
}
}
init(someProperty: AnyObject) {
defer { self.someProperty = someProperty }
}
func doStuff() {
// do stuff now that someProperty is set
}
}
Cela fonctionne si vous faites cela dans une sous-classe
_class Base {
var someProperty: AnyObject {
didSet {
doStuff()
}
}
required init() {
someProperty = "hello"
}
func doStuff() {
print(someProperty)
}
}
class SomeClass: Base {
required init() {
super.init()
someProperty = "hello"
}
}
let a = Base()
let b = SomeClass()
_
Dans l'exemple a
, didSet
n'est pas déclenché. Mais dans l'exemple b
, didSet
est déclenché, car il se trouve dans la sous-classe. Cela doit faire quelque chose avec ce que _initialization context
_ signifie réellement, dans ce cas, la superclass
se souciait de ça
Dans le cas particulier où vous souhaitez invoquer willSet
ou didSet
à l'intérieur de init
pour une propriété disponible dans votre superclasse, vous pouvez simplement affecter votre propriété super directement:
override init(frame: CGRect) {
super.init(frame: frame)
// this will call `willSet` and `didSet`
someProperty = super.someProperty
}
Notez que Charlesism une solution avec une fermeture fonctionnerait toujours aussi dans ce cas. Donc, ma solution est juste une alternative.
Bien que cela ne soit pas une solution, une autre façon de procéder consiste à utiliser un constructeur de classe:
class SomeClass {
var someProperty: AnyObject {
didSet {
// do stuff
}
}
class func createInstance(someProperty: AnyObject) -> SomeClass {
let instance = SomeClass()
instance.someProperty = someProperty
return instance
}
}