Every language has the concept of attributes. What about attributes in Swift?

A, attributes,

Instance-related attributes in Swift can be divided into two categories: storage attributes and computed attributes.

1.1. Storage Properties (Stored Property)

Features:

  • Similar to the concept of a member variable;
  • Stored in the instance’s memory;
  • Structures and classes can define storage properties;
  • Enumerations cannot define storage properties.

Sample code:

struct Circle {
    var radius: Double
}

class classCircle {
    var radius: Double
}
Copy the code

Swift has a clear rule about storage properties: When creating an instance of a class or structure, you must set an appropriate initial value for all storage properties.

  • You can set an initial value for the storage property in the initializer.
  • You can assign a default property value as part of the property definition.

1.2. Calculated attributes (Computed Property)

Features:

  • The essence is method (function);
  • Does not occupy the memory of the instance;
  • Enumerations, structures, and classes can all define computed properties.

Sample code:

Struct diameter: struct diameter: struct diameter: Double { set { print("set") radius = newValue / 2 } get { print("get") return radius * 2 } } } var c = Circle(radius: C. adius = 11 print("--1--") c.diameter = 40 print("--2--") print(c.diameter) /*Copy the code

Output analysis: if the above code executes c.diameter = 40, the radius value will change to 20. Parsed diameter set (40) : parsed diameter set (40) : parsed diameter set (40) : parsed diameter set (40) : parsed diameter set (40) The complete set method code should be set(newValue) {… }, newValue is the default value, which can be modified according to your specification (the default parameter name is recommended). Diameter get c. Diameter get

Memory analysis: How much memory is occupied by the Circle structure in the above example code?

Print (MemoryLayout<Circle>. Stride) // output: 8Copy the code

The result shows 8 bytes. Because the nature of computed properties is methods.

Supplementary notes:

  1. The newValue passed in by set is called newValue by default and can be customized.

    struct Circle {
        var radius: Double
        var diameter: Double {
            set(newDiameter) {
                radius = newDiameter / 2
            }
            get {
                return radius * 2
            }
        }
    }
    Copy the code
  2. Read-only computing properties: Only GET, no set.

Struct Circle {var radius: Double var diameter: Double {radius * 2}} 'struct Circle {var radius: Double {radius * 2}}'Copy the code
  1. Defining computed attributes can only be usedvarAnd don’t uselet.

  1. There aresetYou must haveget.

Extension: Enumerations of rawValues are essentially read-only computed properties.

1.3. Property Observer

The name is reminiscent of KVO in OC, and yes, there are similarities. Property observers can be set in Swift for non-lazy VAR storage properties.

Sample code:

struct Circle { var radius: Double { willSet { print("willSet", newValue) } didSet { print("didSet", oldValue, Radius)}} init() {self.radius = 2.0 print("Circle init ")}} var c = Circle() WillSet 3.0 didSet 2.0 3.0 */Copy the code

Analysis:

  • willSetWill pass the new value, default is callednewValue;
  • didSetWill pass the old value, default is calledoldValue;
  • Setting a property value in an initializer does not triggerwillSetanddidSet. Setting an initial value at property definition also does not trigger.

Lazy Stored Property

Using lazy, you can define a lazy storage property that is initialized only when the property is first used.

Features:

  • lazyThe property must bevar, not alet(letMust have a value before the instance’s initialization method completes);
  • If multiple threads access simultaneously for the first timelazyProperty, there is no guarantee that the property will only be initialized once (non-thread-safe).

Sample code:

class Car { init() { print("Car init") } func run() { print("car run") } } class Person { lazy var car = Car() init() { Print ("Person init")} func goOut() {print("Person goOut") car.run()}} var p = Person() Person init p.goout () /* Output: Person goOut Car init Car run */Copy the code

Analysis: If the storage property car in Person has no lazy modification, the initialization method of the storage property car is called when the Person object P is created. Adding the lazy modifier only initializes the car property (object) the first time it is used.

Note:Only when the structure contains a deferred storage propertyvarTo access the deferred storage properties. The delay attribute initialization requires changing the memory of the struct, whereas the struct if usedletYou can’t modify the memory you’re in.

Type Property

Strictly speaking, attributes can be divided into:

  • Instance Property: Can only be accessed by Instance

    • Stored Instance Property: The Property is Stored in the memory of the Instance. Each Instance has one copy.
    • Computed Instance Property
  • Type Property: Can only be accessed by Type

    • Stored Type Property: During the entire run of the program, only one portion of memory is Stored (similar to global variables).
    • Computed Instance Properties (Computed Type Property)

You can define type attributes through static. If it is a class, the keyword class can also be used.

Sample code:

struct Shape { var width: Int static var count: Int = 30 } var s = Shape(width: 10) s.witchth = 20 print("before count:\(shape.count)") Before count:30 shape. count = 40 print("after count:\(shape.count)") // after count:40Copy the code

3.1. Type attribute details

  1. Unlike store instance attributes, store type attributes must be initialized or an error will be reported (because the type does not have instance attributes)initInitializer to initialize storage properties) :

  1. The storage type attribute is lazy by default and is initialized only when used for the first time, even if accessed by multiple threads at the same time (thread-safe).

  2. The storage type attribute can be let.

  3. Enumerated types can also define type properties (store type properties, evaluate type properties).

3.2. Singleton pattern

You can create a singleton pattern using type attributes. Sample code:

class FileManager {
    public static let shared = FileHandle()
    private init() {}
}
var f1 = FileManager.shared;
Copy the code

Make the initializer private, so you can’t let the outside world create instances using init. Making the type property public, accessible in other files, and storing the type property with a let modifier ensures that the instance can only point to a fixed block of memory.

3.2. The nature of type storage properties

Step 1: Sample code

Step 2: Check the global variable memory address

Num1 Memory address: 0x1000013F1 + 0x5DF7 = 0x1000071E8; Num2 Memory address: 0x1000013FC + 0x5DF4 = 0x1000071F0; Num3 Memory address: 0x100001407 + 0x5DF1 = 0x1000071F8.

Conclusion: num1,num2, and num3 are contiguous memory addresses.

Step 3: View the type storage property address

Num1 Memory address: 0x100001013 + 0x631D = 0x100007330; Car. Count Memory address: 0x100007338; Num3 Memory address: 0x10000105C + 0x62E4 = 0x100007340.

Conclusion: num1, car. count, and num3 are contiguous memory addresses.

From an internal perspective, there is no difference between writing a type storage property outside and inside the class; writing it inside the class simply means that the property has some access to it.

The type store attribute is lazy by default, so it does a lot of work on the first access. And it’s only initialized once.

View type store property initialization by assembly:

It turns out that the type attribute initialization ends with a call to dispatch_once in GCD, which ensures that the attribute is initialized only once.