¿Es posible permitir que se llame a didSet durante la inicialización en Swift?

Pregunta

Los documentos de Apple especifican que:

Los observadores willSet y didSet no se invocan cuando una propiedad se inicializa por primera vez. Solo se llaman cuando el valor de la propiedad se establece fuera de un contexto de inicialización.

¿Es posible forzar que se llamen durante la inicialización?

¿Por qué?

Digamos que tengo esta clase

class SomeClass { var someProperty: AnyObject { didSet { doStuff() } } init(someProperty: AnyObject) { self.someProperty = someProperty doStuff() } func doStuff() { // do stuff now that someProperty is set } } 

doStuff el método doStuff , para hacer las llamadas de procesamiento más concisas, pero preferiría simplemente procesar la propiedad dentro de la función didSet . ¿Hay alguna manera de forzar esto a llamar durante la inicialización?

Actualizar

Decidí simplemente eliminar el intializador de conveniencia para mi clase y forzarlo a establecer la propiedad después de la inicialización. Esto me permite saber que siempre se llamará a didSet . No he decidido si esto es mejor en general, pero se adapta bien a mi situación.

Cree un propio set-Method y úselo dentro de su método init:

 class SomeClass { var someProperty: AnyObject! { didSet { //do some Stuff } } init(someProperty: AnyObject) { setSomeProperty(someProperty) } func setSomeProperty(newValue:AnyObject) { self.someProperty = newValue } } 

Si usa diferir dentro de un inicializador, para actualizar cualquier propiedad opcional o actualizar propiedades no opcionales que ya ha inicializado y después de haber llamado a cualquier método de superinicio, se invocará su willSet, didSet, etc. Encuentro que esto es más conveniente que la implementación de métodos separados que usted debe hacer un seguimiento de las llamadas en los lugares correctos.

Por ejemplo:

 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 } } } 

rendirá:

 I'm going to change to 3.14 Now I'm 3.14 

Como una variación de la respuesta de Oliver, podrías envolver las líneas en un cierre. P.ej:

 class Classy { var foo: Int! { didSet { doStuff() } } init( foo: Int ) { // closure invokes didSet ({ self.foo = foo })() } } 

Editar: La respuesta de Brian Westphal es más agradable. Lo bueno de él es que insinúa la intención.

Tuve el mismo problema y esto funciona para mí

 class SomeClass { var someProperty: AnyObject { didSet { doStuff() } } init(someProperty: AnyObject) { defer { self.someProperty = someProperty } } func doStuff() { // do stuff now that someProperty is set } } 

Esto funciona si haces esto en una subclase

 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() 

En a ejemplo, didSet no se desencadena. Pero en el ejemplo b , didSet se desencadena, porque está en la subclase. Tiene que hacer algo con lo que realmente significa el initialization context , en este caso la superclass preocupó por eso

Si bien esto no es una solución, una forma alternativa de hacerlo sería usar un constructor de clase:

 class SomeClass { var someProperty: AnyObject { didSet { // do stuff } } class func createInstance(someProperty: AnyObject) -> SomeClass { let instance = SomeClass() instance.someProperty = someProperty return instance } } 

En el caso particular en que desee invocar willSet o didSet dentro de init para una propiedad disponible en su superclase, puede simplemente asignar su propiedad súper directamente:

 override init(frame: CGRect) { super.init(frame: frame) // this will call `willSet` and `didSet` someProperty = super.someProperty } 

Tenga en cuenta que la solución de Charlesism con un cierre siempre funcionaría también en ese caso. Entonces mi solución es solo una alternativa.

Puedes resolverlo de manera obj-с:

 class SomeClass { private var _someProperty: AnyObject! var someProperty: AnyObject{ get{ return _someProperty } set{ _someProperty = newValue doStuff() } } init(someProperty: AnyObject) { self.someProperty = someProperty doStuff() } func doStuff() { // do stuff now that someProperty is set } } 
Intereting Posts