Painted levels: being fostered fostered fostered

Tags: “iOS” “Swift 5.1” “Storage Properties” “Computing Properties” “Property Observation” author: Mu Ling Lo Review: QiShare team


attribute

Attributes associate values with a particular class, structure, or enumeration. Store properties: Store values, constants and variable values as part of the instance. Storage properties are supported only by classes and structs. Computed properties: Computed values, computed properties are supported by classes, structs, and enumerations.

Storage properties

Stored properties can be variable-stored properties declared by the var keyword, or constant-stored properties declared by the let keyword. Examples of structure storage property definitions:

Struct StructEnumType {//! Define a constant storage property let constantPro: Int //! Var varPro: Int //! The < 'var' keyword can declare attributes like this, but must be assigned in the initialization method. Var enumType = StructEnumType(constantPro: 0, varPro: 0); Enumtype. constantPro = 0 //! < compiler error: Cannot assign to property: 'constantPro' is a 'let' constant < Change success. StructEnumType(constantPro: 0, varPro: 6)Copy the code

Storage properties of a constant struct instance

Let enumType = StructEnumType(constantPro: 0, varPro: 0) Enumtype. constantPro = 0 //! < compiler error: Cannot assign to property: 'constantPro' is a 'let' constant Cannot assign to property: 'enumType' is a 'let' constantCopy the code

StructEnumType defines variable and constant attributes, respectively.

• Use var to define a variable instance of StructEnumType that cannot modify constant properties in the structure, but can modify the value of its variable properties.

• Use let to define constant instances of StructEnumType that cannot modify constant and variable properties in the structure.

Conclusion: A structure is a value type, and all properties of a token value type are marked as constants when instances of the token value type are marked as constants. Example of a storage property definition for a class:

Var classVarPro: Int let classConstantPro: Int = 8 override init() {classVarPro = 7 super.init()} var classObj = PropertiesClass( classObj.classConstantPro = 7 //! < compiler error: Cannot assign to property: 'constantPro' is a 'let' constant classObj. ClassVarPro = 10 //! < Correct outputCopy the code

• A class is a reference type. You can change the value of a variable attribute using either a variable instance or a constant instance of the class, but you cannot change the value of a constant attribute.

Lazy storage property

Lazy storage property: its initial value is not called until it is first used. Similar to lazy loading. Declare lazy storage properties: Use the keyword lazy. Note: Lazy attributes must be declared as variable attributes using the var keyword.

Store properties and instance variables

Objective-c provides two ways to store values and references as part of a class instance. In addition to attributes, instance variables can be used. There are only properties in Swift and properties in Swift have no corresponding instance variables.

Calculate attribute

Classes, structures, and enumerated types can define computed properties that provide a getter and an optional setter to indirectly obtain and set other properties and values. Only the var keyword can be used for the declaration of the evaluated property

/ /! Struct Point {var x = 0,y = 0} // Struct Size {var width = 0,height = 0} /* Struct Size {var width = 0,height = 0} /* Rect Size; So we will define a calculated property in 'rect'. */ struct rect {var origin = Point() var size = size () var origin = Point() var size = size () Get and set both need to be present, 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(customValue){// 'customValue' is the point where the new value is originate. x = customValue.x - (sie.width / 2) originate. y = customValue.y - (size.height / 2) } } }Copy the code

Define the structure of a RECT: A RECT will have an origin, will have a size, according to these two storage properties, can calculate the center. So we define a calculation property center in recT. The getter and setter methods provided by the center are used to obtain the attribute value and the correlation processing of the attribute value. Call as follows:

var frame = rect(origin: Point(x: 2, y: 2), size: Size.init(width: 10, height: 10)) //! < cannot use 'let'.. frame.center = Point.init(x: 10, y: 10) //! Point(x: 7, y: 7) print(" set: \(frame.center)")//Point(x: 10, y: 10)Copy the code

When frame.center is used, the getter method of the center calculation property in the rect structure is called. Using frame.center = point.init (x: 10, y: 10) calls setter methods to set the property value associated with getting the center property value. Getter methods are only needed if the external evaluation of the Center property is just a fetch, and setters are also necessary if it is not only a fetch but also a reset.

A setter method declaration for short

If the setter method that evaluates the property does not define a name for the newValue to set, the default name newValue is used

struct rect { var origin = Point() var size = Size() var center : Point { get { let centerX = origin.x + (size.width / 2) let centerY = origin.y + (size.height / 2) return Point(x: centerX, y: X = newvalue. x - (size.width / 2) origin.y = newvalue. y -) centerY)} set{// 'newValue' is the point where the newValue is assigned (size.height / 2) } } }Copy the code

Read-only computed property

Read-only computed property: a computed property that has a getter but no setter methods. The read-only calculation property always returns a value that can be accessed through point syntax but cannot be set to any other value. Note: Computed properties, including read-only computed properties, must be declared as variable properties using the var keyword because the value of computed properties is not fixed. The let keyword is only used for constant properties, and once they are set as part of the instance initialization, their values cannot be changed.

/ /! Struct areaStruct {var width = 0.0,height = 0.0 var area:Double {get {return width * height}}Copy the code

The declaration of read-only computed properties can also be simplified by removing the get keyword and its {}

Struct areaStruct {var width = 0.0,height = 0.0 var area:Double {return width * height}} /* All the variable properties inside it will become constant properties. That is, setter methods are not available, but getter methods are not affected. */ let area = areastruct. init(width: 20.0, height: 3.0) print("\(area.area)")Copy the code

Attribute observer

Attribute observer: Watches and responds to changes in attribute values. The property observer is called each time the property’s value is set, even if the new value is the same as the property’s current value. We can add attribute observers to any stored property we define, except for lazy stored properties, which are variable properties decorated with lazy. You can also add a property observer to a property inherited from the parent class by overriding it in a subclass (either a store property or a compute property). Attribute observer definition:

• willSet is called before storing new values

• After didSet stores new values the call can define either willSet or didSet, or both, in the property.

•willSet The observer method, which passes the value of the new property as a constant parameter. You can specify the name of the constant parameter in the method implementation. If the parameter name is not specified in the implementation, the default parameter name newValue will be used.

•didSet This observer method, which passes the old property value as a constant parameter. You can specify the name of the constant parameter in the method implementation. If the parameter name is not specified in the implementation, the default parameter name oldValue is used. Also: If, within the didSet observer method, a new value is assigned to this property, the new value will replace the value just set. The willSet and didSet observer methods of the parent attribute are called when the parent class’s initialization method is called and the parent class’s attributes are set in the child class’s initialization method. The willSet and didSet observer methods are not called when the class sets its own properties until the parent class’s initialization methods are called.

// Class propertyObeserver: NSObject {var progress = 0 {willSet(newProgress) {print(" New value to be set \(newProgress)")} // willSet {// Print (" newValue to set \(newValue)") //} didSet(oldProgress){print(" newValue to set \(progress-oldprogress)")} // didSet {// Print (" set new value and oldValue difference \(progress-oldvalue)") //}}} // use // property observer let propertyObj = propertyobeserver.init () propertyObj.progress = 200 //! < new value to set 200; Progress = 100 //! < The new value to setCopy the code

Note: If you pass a property with an observer to a function as an input and output parameter, the willSet and didSet observer methods are always called. Because of the copy-in copy-out memory model for in-out parameters: the value of the property that was the parameter at the end of the function is always written back into the original property.

Copy-in copy-out: the copy-in copy-out behavior is also called calling results by value. The specific behavior is as follows:

• When this function is called, the value of the inout parameter is copied.

• In the function body, a copy of the parameter value is modified

• When the function returns, a copy of the parameter value is assigned to the original parameter.

For example, when computed properties or properties with observers are passed as input and output arguments to a function, their getter methods are called as part of the function call. Their setters are called as part of the function return. As an optimization, when parameter values are physical addresses stored in memory, the same memory area is used inside and outside the function. This optimization is called reference invocation. It satisfies all the requirements of copy-in copy-out model and eliminates the overhead of copying. Write code using the copy-in copy-out model, without relying on invoking optimization by reference, so that copy-in copy-out behaves correctly with or without optimization. Do not access values passed as input and output parameters within a function, even if the original value is available in the current range. Because it violates Swift’s memory exclusivity, it is also impossible to pass the same value to multiple input and output parameters.

var stepSize = 1 func increment(_ number: inout Int) { number += stepSize } increment(&stepSize)//! < Error: Access stepSize conflictCopy the code

A closure or nested function that captures input and output parameters must be nonescapable. If you want to capture input and output parameters without changing them, or if you want to observe changes made by other code, you need to use capture lists to explicitly capture parameters in an immutable way.

static func someFunction1(a: Inout Int) -> () -> Int {return {[a] in return a + 1}} var b = 7 let c = someFunction1(a: &b)() print("\(b)... \(c)")//7... 8Copy the code

Example above: the function someFunction1 takes the input and output parameter a, and the return value of the function is () -> Int. In the return value of someFunction1 function, input and output parameter A needs to be captured and called outside of someFunction1 function. Therefore, the form of capture list [a] is used to capture the input and output parameter. If you need to capture and change input and output parameters, you need to use an explicit local copy.

Static func mutateFuncation(x:inout Int) ->Void{static func mutateFuncation(x:inout Int) ->Void{ So define a local copy var localX = x // that requires the defer keyword modifier to be called before the function ends, once again changing the copy, assigning the original parameter 'x 'defer {/*< The 'defer' statement before ending the range always executes immediately. */ x = localX} changeValue(b: &localx)} static func changeValue(b: &localx)} static func changeValue(b: Inout Int) {b += 3} var b = 7 mutateFuncation(x: &b) print(" variable call result \(b)") < Variable call result 10Copy the code

Example above: The function mutateFuncation has an input and output parameter x that is changed inside the function by the changeValue function, so the input and output parameter X is changed inside the function mutateFuncation. So an explicit local copy is needed. And define the defer{} code block so that it can be assigned to the original value once the copy has been modified.

Global and local variables

Computed properties and properties with observers can be used for global and local variables. You can also define computed variables at global or local scope, and you can define observers for stored variables. Computed variables are defined in the same way as computed properties. Global variables: variables defined outside the context of any function, method, closure, or type. Local variables: variables defined in the context of a function, method, or closure.

The type attribute

Instance properties: Properties of an instance of a particular type. Each time a new instance of a particular type is created, it has its own set of property values that are distinct from other instances. Type attributes: Attributes that belong to the type itself. No matter how many instances of the type we create, there is only one copy of the type attributes. Type attributes can define values that are common to all instances of a particular type. Examples are constant properties that all instances can use (such as static constants in C), or variable properties that store global values (such as static variables in C) that all instances can use. Stored type attributes can be variables or constants. Computed type properties are always declared as variable properties in the same way that instance properties are computed.

Type attribute syntax

In C and Objective-C, type attributes define static constants and variables associated with a type as global static variables. Referring to Swift, type attributes are defined as part of the type definition, outside of the type {}, and each type attribute is explicitly qualified to the type it supports. Use the static keyword to define type attributes. For computed type attributes of class types, you can use the class keyword instead of static to define type attributes, allowing subclasses to override the implementation of the parent class.

struct SomeStructure { static var storedTypeProperty = "Some value." static var computedTypeProperty: Int { return 1 } } enum SomeEnumeration { static var storedTypeProperty = "Some value." static var computedTypeProperty:  Int { return 6 } } class SomeClass { static var storedTypeProperty = "Some value." static var computedTypeProperty: Int { return 27 } class var overrideableComputedTypeProperty: Int { return 107 } } class subSomeClass: SomeClass { //! When the parent class practical static The rewrite complains here ` always override a static property1 override class var overrideableComputedTypeProperty: Int { return 227 } }Copy the code

Query and set type properties

Use point syntax to query type properties and set type properties, just like instance properties. But a type attribute queries and sets type attributes on a type, not on an instance of that type.

Print (" access storage type attribute of the structure \ [SomeStructure. StoredTypeProperty) ") / /! < structure for storage type attribute value. Some SomeStructure. StoredTypeProperty = "Another value." Print (" set up the structure of storage type attribute \ (SomeStructure. StoredTypeProperty) ") / /! < set structure of storage type attribute value. Another print (" obtain the structure calculation type attribute \ putedTypeProperty (SomeStructure.com) ") / /! < obtain structure calculation type attribute 1 print (" calculation type attributes of an enum type to obtain \ putedTypeProperty (SomeEnumeration.com) ") / /! < calculation type attributes of an enum type to obtain 6 print (" class type for calculation type attribute \ putedTypeProperty (SomeClass.com) ") / /! < class type for calculation type attribute 27 print (" get a subclass of the calculation of the class type type attribute \ (subSomeClass. OverrideableComputedTypeProperty) ") / /! Struct volume {//! Static let maxLevel = 20 //! Static var isNorse: Bool = false //! Var currentLevel: Int = 0 {//! < current volume didSet {if currentLevel > volume.maxLevel {currentLevel = volume.maxLevel} if currentLevel > volume.maxLevel/2  { volume.isNorse = true } else { volume.isNorse = false } } } }Copy the code

Resources: Swift 5.1 official programming guide


Recommended articles:

CGAffineTransform iOS performance monitoring (1) CPU power monitoring iOS performance monitoring (2) Main thread lag monitoring iOS performance monitoring (3) Method Time monitoring First identifies Flutter Web Add animation to view with SwiftUI Write a simple page with SwiftUI iOS App startup optimization (3) — make your own tool to monitor the App startup Time (2) — use the “Time Profiler” tool to monitor the App startup Time iOS App startup optimization (a) – to understand the App startup process qiwu Weekly