willset did and swift

didset and willset in swift



¿Cuál es el propósito de willSet y didSet en Swift? (11)

Los observadores willSet y didSet para las propiedades siempre que se asigne un nuevo valor a la propiedad. Esto es cierto incluso si el nuevo valor es el mismo que el valor actual.

Y tenga en cuenta que willSet necesita un nombre de parámetro para evitar, por otro lado, didSet no.

Se llama al observador didSet después de actualizar el valor de la propiedad. Se compara con el valor antiguo. Si el número total de pasos ha aumentado, se imprime un mensaje para indicar cuántos pasos nuevos se han tomado. El observador didSet no proporciona un nombre de parámetro personalizado para el valor antiguo, y en su lugar se usa el nombre predeterminado de oldValue.

Swift tiene una sintaxis de declaración de propiedad muy similar a la de C #:

var foo: Int { get { return getFoo() } set { setFoo(newValue) } }

Sin embargo, también tiene acciones willSet y didSet . Estos se llaman antes y después de que se llame al colocador, respectivamente. ¿Cuál es su propósito, teniendo en cuenta que podría tener el mismo código dentro del configurador?


NOTA

willSet didSet observadores willSet y didSet cuando se establece una propiedad en un inicializador antes de que tenga lugar la delegación


El getter y el setter son a veces demasiado pesados ​​para implementar solo para observar los cambios de valor apropiados. Por lo general, esto requiere un manejo de variables temporales adicionales y controles adicionales, y usted querrá evitar incluso ese pequeño trabajo si escribe cientos de captadores y configuradores. Estas cosas son para la situación.


El punto parece ser que, a veces, necesita una propiedad que tenga almacenamiento automático y algún comportamiento, por ejemplo, para notificar a otros objetos que la propiedad acaba de cambiar. Cuando todo lo que tiene es get / set , necesita otro campo para mantener el valor. Con willSet y didSet , puede actuar cuando el valor se modifica sin necesidad de otro campo. Por ejemplo, en ese ejemplo:

class Foo { var myProperty: Int = 0 { didSet { print("The value of myProperty changed from /(oldValue) to /(myProperty)") } } }

myProperty imprime su valor antiguo y nuevo cada vez que se modifica. Con solo getters y setters, necesitaría esto en su lugar:

class Foo { var myPropertyValue: Int = 0 var myProperty: Int { get { return myPropertyValue } set { print("The value of myProperty changed from /(myPropertyValue) to /(newValue)") myPropertyValue = newValue } } }

Por willSet tanto, willSet y didSet representan una economía de un par de líneas y menos ruido en la lista de campos.


En su propia clase (base), willSet y didSet son bastante redundantes , ya que en su lugar podría definir una propiedad calculada (es decir, métodos _propertyVariable y _propertyVariable ) que acceden a una _propertyVariable y _propertyVariable la pre- persesión y el post-proceso deseados .

Sin embargo , si anula una clase en la que la propiedad ya está definida , ¡ willSet y didSet son útiles y no redundantes!


Estos se llaman observadores de propiedad :

Los observadores de propiedades observan y responden a cambios en el valor de una propiedad. Se llama a los observadores de propiedades cada vez que se establece el valor de una propiedad, incluso si el nuevo valor es el mismo que el valor actual de la propiedad.

Extracto de: Apple Inc. "El lenguaje de programación Swift". IBooks. https://itun.es/ca/jEUH0.l

Sospecho que es para permitir cosas que tradicionalmente haríamos con KVO , como el enlace de datos con elementos de la interfaz de usuario, o los efectos secundarios desencadenantes de cambiar una propiedad, desencadenar un proceso de sincronización, procesamiento en segundo plano, etc.


Las muchas respuestas existentes bien escritas cubren bien la pregunta, pero mencionaré, con cierto detalle, una adición que creo que vale la pena cubrir.

Los observadores de la propiedad willSet y didSet se pueden usar para llamar a los delegados, por ejemplo, para las propiedades de clase que solo se actualizan mediante la interacción del usuario, pero donde se desea evitar llamar al delegado en la inicialización del objeto.

Citaré el comentario de Klaas a la respuesta aceptada:

Los observadores willSet y didSet no se llaman 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.

Esto es bastante didSet ya que significa que, por ejemplo, la propiedad didSet es una buena opción de punto de inicio para devoluciones de llamadas y funciones de delegado, para sus propias clases personalizadas.

Como ejemplo, considere algún objeto de control de usuario personalizado, con algún value propiedad clave (por ejemplo, la posición en el control de calificación), implementado como una subclase de UIView :

// CustomUserControl.swift protocol CustomUserControlDelegate { func didChangeValue(value: Int) // func didChangeValue(newValue: Int, oldValue: Int) // func didChangeValue(customUserControl: CustomUserControl) // ... other more sophisticated delegate functions } class CustomUserControl: UIView { // Properties // ... private var value = 0 { didSet { // Possibly do something ... // Call delegate. delegate?.didChangeValue(value) // delegate?.didChangeValue(value, oldValue: oldValue) // delegate?.didChangeValue(self) } } var delegate: CustomUserControlDelegate? // Initialization required init?(...) { // Initialise something ... // E.g. ''value = 1'' would not call didSet at this point } // ... some methods/actions associated with your user control. }

Después de lo cual, sus funciones de delegado se pueden usar en, por ejemplo, algunos controladores de vista para observar los cambios clave en el modelo para CustomViewController , como si fuera a usar las funciones de delegado inherentes de UITextFieldDelegate para objetos de UITextField (por ejemplo, textFieldDidEndEditing(...) ) .

Para este ejemplo simple, use una devolución de llamada delegada del didSet del value propiedad de clase para decirle a un controlador de vista que uno de sus puntos de venta ha tenido actualización de modelo asociada:

// ViewController.swift Import UIKit // ... class ViewController: UIViewController, CustomUserControlDelegate { // Properties // ... @IBOutlet weak var customUserControl: CustomUserControl! override func viewDidLoad() { super.viewDidLoad() // ... // Custom user control, handle through delegate callbacks. customUserControl = self } // ... // CustomUserControlDelegate func didChangeValue(value: Int) { // do some stuff with ''value'' ... } // func didChangeValue(newValue: Int, oldValue: Int) { // do some stuff with new as well as old ''value'' ... // custom transitions? :) //} //func didChangeValue(customUserControl: CustomUserControl) { // // Do more advanced stuff ... //} }

Aquí, la propiedad de value se ha encapsulado, pero en general: en situaciones como estas, tenga cuidado de no actualizar la propiedad de value del objeto customUserControl en el alcance de la función de delegado asociada (aquí: didChangeValue() ) en el controlador de vista, o Terminarás con una recursión infinita.


Mi entendimiento es que establecer y obtener son para propiedades computadas (sin respaldo de propiedades almacenadas )

Si viene de un Objective-C, tenga en cuenta que las convenciones de nomenclatura han cambiado. En Swift, una iVar o variable de instancia se denomina propiedad almacenada

Ejemplo 1 (propiedad de solo lectura) - con advertencia:

var test : Int { get { return test } }

Esto dará como resultado una advertencia porque dará como resultado una llamada de función recursiva (el captador se llama a sí mismo). La advertencia en este caso es "Intentar modificar la" prueba "dentro de su propio captador".

Ejemplo 2. Lectura / escritura condicional - con advertencia

var test : Int { get { return test } set (aNewValue) { //I''ve contrived some condition on which this property can be set //(prevents same value being set) if (aNewValue != test) { test = aNewValue } } }

Problema similar: no puede hacer esto porque está recursivamente llamando al configurador. Además, tenga en cuenta que este código no se quejará de que no haya inicializadores, ya que no hay propiedades almacenadas para inicializar .

Ejemplo 3. propiedad computada de lectura / escritura - con almacén de respaldo

Aquí hay un patrón que permite la configuración condicional de una propiedad almacenada real

//True model data var _test : Int = 0 var test : Int { get { return _test } set (aNewValue) { //I''ve contrived some condition on which this property can be set if (aNewValue != test) { _test = aNewValue } } }

Nota Los datos reales se llaman _test (aunque podría ser cualquier dato o combinación de datos). Tenga en cuenta también la necesidad de proporcionar un valor inicial (como alternativa, debe usar un método init) porque _test es en realidad una variable de instancia

Ejemplo 4. Usando will y set

//True model data var _test : Int = 0 { //First this willSet { println("Old value is /(_test), new value is /(newValue)") } //value is set //Finaly this didSet { println("Old value is /(oldValue), new value is /(_test)") } } var test : Int { get { return _test } set (aNewValue) { //I''ve contrived some condition on which this property can be set if (aNewValue != test) { _test = aNewValue } } }

Aquí vemos que willSet e didSet interceptan un cambio en una propiedad real almacenada. Esto es útil para enviar notificaciones, sincronización, etc. (vea el ejemplo a continuación)

Ejemplo 5. Ejemplo concreto - ViewController Container

//Underlying instance variable (would ideally be private) var _childVC : UIViewController? { willSet { //REMOVE OLD VC println("Property will set") if (_childVC != nil) { _childVC!.willMoveToParentViewController(nil) self.setOverrideTraitCollection(nil, forChildViewController: _childVC) _childVC!.view.removeFromSuperview() _childVC!.removeFromParentViewController() } if (newValue) { self.addChildViewController(newValue) } } //I can''t see a way to ''stop'' the value being set to the same controller - hence the computed property didSet { //ADD NEW VC println("Property did set") if (_childVC) { // var views = NSDictionaryOfVariableBindings(self.view) .. NOT YET SUPPORTED (NSDictionary bridging not yet available) //Add subviews + constraints _childVC!.view.setTranslatesAutoresizingMaskIntoConstraints(false) //For now - until I add my own constraints self.view.addSubview(_childVC!.view) let views = ["view" : _childVC!.view] as NSMutableDictionary let layoutOpts = NSLayoutFormatOptions(0) let lc1 : AnyObject[] = NSLayoutConstraint.constraintsWithVisualFormat("|[view]|", options: layoutOpts, metrics: NSDictionary(), views: views) let lc2 : AnyObject[] = NSLayoutConstraint.constraintsWithVisualFormat("V:|[view]|", options: layoutOpts, metrics: NSDictionary(), views: views) self.view.addConstraints(lc1) self.view.addConstraints(lc2) //Forward messages to child _childVC!.didMoveToParentViewController(self) } } } //Computed property - this is the property that must be used to prevent setting the same value twice //unless there is another way of doing this? var childVC : UIViewController? { get { return _childVC } set(suggestedVC) { if (suggestedVC != _childVC) { _childVC = suggestedVC } } }

Tenga en cuenta el uso de ambas propiedades calculadas y almacenadas. He usado una propiedad computada para evitar establecer el mismo valor dos veces (¡para evitar que sucedan cosas malas!); He usado willSet y didSet para reenviar notificaciones a viewControllers (consulte la documentación e información de UIViewController en los contenedores de viewController)

Espero que esto ayude, y por favor, alguien grite si he cometido un error en alguna parte aquí!


No sé C #, pero con un poco de conjetura creo que entiendo qué

foo : int { get { return getFoo(); } set { setFoo(newValue); } }

hace. Parece muy similar a lo que tienes en Swift, pero no es lo mismo: en Swift no tienes el getFoo y setFoo . Esa no es una pequeña diferencia: significa que no tiene ningún almacenamiento subyacente para su valor.

Swift ha almacenado y computado propiedades.

Una propiedad calculada tiene get y puede haberse set (si se puede escribir). Pero el código en el getter y setter, si necesitan almacenar realmente algunos datos, deben hacerlo en otras propiedades. No hay almacenamiento de respaldo.

Una propiedad almacenada, por otro lado, tiene almacenamiento de respaldo. Pero no tiene get y set . En su lugar, tiene willSet y didSet que puede utilizar para observar cambios de variables y, eventualmente, desencadenar efectos secundarios y / o modificar el valor almacenado. No tiene willSet y didSet para las propiedades computadas, y no las necesita porque para las propiedades computadas puede usar el código en set para controlar los cambios.


También puede usar didSet para establecer la variable en un valor diferente. Esto no hace que se vuelva a llamar al observador como se indica en la Guía de propiedades . Por ejemplo, es útil cuando desea limitar el valor de la siguiente manera:

let minValue = 1 var value = 1 { didSet { if value < minValue { value = minValue } } } value = -10 // value is minValue now.


Una cosa en la que didSet es realmente útil es cuando se usan puntos de venta para agregar configuración adicional.

@IBOutlet weak var loginOrSignupButton: UIButton! { didSet { let title = NSLocalizedString("signup_required_button") loginOrSignupButton.setTitle(title, for: .normal) loginOrSignupButton.setTitle(title, for: .highlighted) }