| .. @raise litre.TestsAreMissing |
| |
| ============================= |
| Stored and Computed Variables |
| ============================= |
| |
| .. warning:: This document has not been updated since the initial design in |
| Swift 1.0. |
| |
| |
| Variables are declared using the ``var`` keyword. These declarations are valid |
| at the top level, within types, and within code bodies, and are respectively |
| known as *global variables,* *member variables,* and *local variables.* |
| Member variables are commonly referred to as *properties.* |
| |
| Every variable declaration can be classified as either *stored* or *computed.* |
| Member variables inherited from a superclass obey slightly different rules. |
| |
| .. contents:: :local: |
| |
| |
| Stored Variables |
| ================ |
| |
| The simplest form of a variable declaration provides only a type:: |
| |
| var count : Int |
| |
| This form of ``var`` declares a *stored variable.* Stored variables cause |
| storage to be allocated in their containing context: |
| |
| - a new global symbol for a global variable |
| - a slot in an object for a member variable |
| - space on the stack for a local variable |
| |
| (Note that this storage may still be optimized away if determined unnecessary.) |
| |
| Stored variables must be initialized before use. As such, an initial value can |
| be provided at the declaration site. This is mandatory for global variables, |
| since it cannot be proven who accesses the variable first. :: |
| |
| var count : Int = 10 |
| |
| If the type of the variable can be inferred from the initial value expression, |
| it may be omitted in the declaration:: |
| |
| var count = 10 |
| |
| Variables formed during pattern matching are also considered stored |
| variables. :: |
| |
| switch optVal { |
| case .Some(var actualVal): |
| // do something |
| case .None: |
| // do something else |
| } |
| |
| |
| Computed Variables |
| ================== |
| |
| A *computed variable* behaves syntactically like a variable, but does not |
| actually require storage. Instead, accesses to the variable go through |
| "accessors" known as the *getter* and the *setter.* Thus, a computed variable |
| is declared as a variable with a custom getter:: |
| |
| struct Rect { |
| // Stored member variables |
| var x, y, width, height : Int |
| |
| // A computed member variable |
| var maxX : Int { |
| get { |
| return x + width |
| } |
| set(newMax) { |
| x = newMax - width |
| } |
| } |
| |
| // myRect.maxX = 40 |
| |
| In this example, no storage is provided for ``maxX``. |
| |
| If the setter's argument is omitted, it is assumed to be named ``value``:: |
| |
| var maxY : Int { |
| get { |
| return y + height |
| } |
| set { |
| y = value - height |
| } |
| } |
| |
| Finally, if a computed variable has a getter but no setter, it becomes a |
| *read-only variable.* In this case the ``get`` label may be omitted. |
| Attempting to set a read-only variable is a compile-time error:: |
| |
| var area : Int { |
| return self.width * self.height |
| } |
| } |
| |
| Note that because this is a member variable, the implicit parameter ``self`` is |
| available for use within the accessors. |
| |
| It is illegal for a variable to have a setter but no getter. |
| |
| |
| Observing Accessors |
| =================== |
| |
| Occasionally it is useful to provide custom behavior when changing a variable's |
| value that goes beyond simply modifying the underlying storage. One way to do |
| this is to pair a stored variable with a computed variable:: |
| |
| var _backgroundColor : Color |
| var backgroundColor : Color { |
| get { |
| return _backgroundColor |
| } |
| set { |
| _backgroundColor = value |
| refresh() |
| } |
| } |
| |
| However, this contains a fair amount of boilerplate. For cases where a stored |
| property provides the correct storage semantics, you can add custom behavior |
| before or after the underlying assignment using "observing accessors" |
| ``willSet`` and ``didSet``:: |
| |
| var backgroundColor : Color { |
| didSet { |
| refresh() |
| } |
| } |
| |
| var currentURL : URL { |
| willSet(newValue) { |
| if newValue != currentURL { |
| cancelCurrentRequest() |
| } |
| } |
| didSet { |
| sendNewRequest(currentURL) |
| } |
| } |
| |
| A stored property may have either observing accessor, or both. Like ``set``, |
| the argument for ``willSet`` may be omitted, in which case it is provided as |
| "value":: |
| |
| var accountName : String { |
| willSet { |
| assert(value != "root") |
| } |
| } |
| |
| Observing accessors provide the same behavior as the two-variable example, with |
| two important exceptions: |
| |
| - A variable with observing accessors is still a stored variable, which means |
| it must still be initialized before use. Initialization does not run the |
| code in the observing accessors. |
| - All assignments to the variable will trigger the observing accessors with |
| the following exceptions: assignments in the init and destructor function for |
| the enclosing type, and those from within the accessors themselves. In this |
| context, assignments directly store to the underlying storage. |
| |
| Computed properties may not have observing accessors. That is, a property may |
| have a custom getter or observing accessors, but not both. |
| |
| |
| Overriding Read-Only Variables |
| ============================== |
| |
| If a member variable within a class is a read-only computed variable, it may |
| be overridden by subclasses. In this case, the subclass may choose to replace |
| that computed variable with a stored variable by declaring the stored variable |
| in the usual way:: |
| |
| class Base { |
| var color : Color { |
| return .Black |
| } |
| } |
| |
| class Colorful : Base { |
| var color : Color |
| } |
| |
| var object = Colorful(.Red) |
| object.color = .Blue |
| |
| The new stored variable may have observing accessors:: |
| |
| class MemoryColorful : Base { |
| var oldColors : Array<Color> = [] |
| |
| var color : Color { |
| willSet { |
| oldColors.append(color) |
| } |
| } |
| } |
| |
| A computed variable may also be overridden with another computed variable:: |
| |
| class MaybeColorful : Base { |
| var color : Color { |
| get { |
| if randomBooleanValue() { |
| return .Green |
| } else { |
| return super.color |
| } |
| } |
| set { |
| print("Sorry, we choose our own colors here.") |
| } |
| } |
| } |
| |
| |
| Overriding Read-Write Variables |
| =============================== |
| |
| If a member variable within a class as a read-write variable, it is not |
| generally possible to know if it is a computed variable or stored variable. |
| A subclass may override the superclass's variable with a new computed variable:: |
| |
| class ColorBase { |
| var color : Color { |
| didSet { |
| print("I've been painted \(color)!") |
| } |
| } |
| } |
| |
| class BrightlyColored : ColorBase { |
| var color : Color { |
| get { |
| return super.color |
| } |
| set(newColor) { |
| // Prefer whichever color is brighter. |
| if newColor.luminance > super.color.luminance { |
| super.color = newColor |
| } else { |
| // Keep the old color. |
| } |
| } |
| } |
| } |
| |
| In this case, because the superclass's ``didSet`` is part of the generated |
| setter, it is only called when the subclass actually invokes setter through |
| its superclass. On the ``else`` branch, the superclass's ``didSet`` is skipped. |
| |
| A subclass may also use observing accessors to add behavior to an inherited |
| member variable:: |
| |
| class TrackingColored : ColorBase { |
| var prevColor : Color? |
| |
| var color : Color { |
| willSet { |
| prevColor = color |
| } |
| } |
| } |
| |
| In this case, the ``willSet`` accessor in the subclass is called first, then |
| the setter for ``color`` in the superclass. Critically, this is *not* declaring |
| a new stored variable, and the subclass will *not* need to initialize ``color`` |
| as a separate member variable. |
| |
| Because observing accessors add behavior to an inherited member variable, a |
| superclass's variable may not be overridden with a new stored variable, even |
| if no observing accessors are specified. In the rare case where this is |
| desired, the two-variable pattern shown above__ can be used. |
| |
| __ `Observing Accessors`_ |
| |