Enumeration (enum)

The member type of the enumeration

As opposed to an enumeration of OC. Enumerations in Swift are much more powerful

Enumeration values in OC can only be ints. Enumerations in Swift can be int,char,String. Values in the same enumeration can also be of different types.

RawValue of enumeration

When we type an enumeration, the members of the enumeration must all be of the same type. We can also associate each enumerated value with a default value.

When set to String and Int, Swift automatically sets the raw value for the enumerator

enum Test : String{
    case test1 = "test1"
    case test2 = "test2"
}
enum Test1 : String{
    case test1
    case test2
}
print(Test.test1.rawValue)		//test1
print(Test1.test1.rawValue)		//test1

enum TestNumber:Int {
    case first
    case seconde
}

enum TestNumber1:Int {
    case first = 0
    case seconde = 1
}
print(TestNumber.first.rawValue)		//0
print(TestNumber1.first.rawValue)		//0
Copy the code

The associated value of an enumeration

Member values of enumerations are sometimes stored in association with values of other types.

enum Time {
    case intValue(hour:Int,min:Int,sec:Int)
    case stringValue(String)
}
var time = Time.intValue(hour: 10, min: 10, sec: 10)
time = .stringValue("10:20:21")
Copy the code

Memory allocation for enumeration

No enumeration of associated values. The memory used in multiple cases is 1 byte. Use only to store enumeration member values. The raw value of the enumeration, however, is obtained by other means. If there is only one case

enum Test { case number case other } MemoryLayout<Test>.size //1 MemoryLayout<Test>.stride //1 MemoryLayout<Test>. Alignment //1 var T = test. number // Memory distribution //00 T = test. other //01 enum Test1 {case number} MemoryLayout<Test1>.size //0 MemoryLayout<Test1>.stride //1 MemoryLayout<Test1>.alignment //1Copy the code

Enumerations of associated values exist. The number of bytes occupied in memory is the size of the maximum associated value of occupied bytes +1.

The last byte holds the enumerated member value. You can also view it as a serial number. Indicates which case is currently in. The preceding byte holds the associated value

enum Test { case number(Int, Int, Int, Int) case other } MemoryLayout<Test>.size //33 MemoryLayout<Test>.stride //40 MemoryLayout<Test>.alignment //8 var t = Test.number(1, 2, 3, 4)/distributed/memory / / 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 / / 02 / / 03 00 00 00 00 00 00 00 / / 4 00 00 00 00 00 00 00 00 / / //00 00 00 00 00 00 00 t = Test.other //00 00 00 00 00 00 00 00 //00 00 00 00 00 00 00 00 //00 00 00 00 00 00 00 00 //00 00 00 00 00 00 00 00 //01 /00 00 00 00 00 00 00 00 00 00 00 00Copy the code

Structure (struct)

The vast majority of exposed types in SWIFT are structures. Enumerations and classes are a small part

Bool Int Double String Array Dictionary is a structure

Initializer for a structure

The architecture automatically generates the initializer

struct A{
    var a1: Int
    var a2: Int
}
var a1 = A(a1: 10, a2: 10)
var a2 = A(a2: 10)              //Missing argument for parameter 'a1' in call
var a3 = A(a1: 10)              //Missing argument for parameter 'a2' in call
var a4 = A()                    //Missing arguments for parameters 'a1', 'a2' in call

struct C {
    var a:Int = 0
    var b:Int = 0
    var c:Int = 0
    
    func printValue() {
        print(a,b,c)
    }
}
var c1 = C(a: 10, b: 10, c: 10)
var c2 = C(a: 10)
var c3 = C(b: 10)
var c4 = C(c: 10)
var c5 = C()
c1.printValue()     //10 10 10
c2.printValue()     //10 0 0
c3.printValue()     //0 10 0
c4.printValue()     //0 0 10
c5.printValue()     //0 0 0

struct C1 {
    var a:Int
    var b:Int
    var c:Int
    init() {
        self.a = 0;
        self.b = 0;
        self.c = 0;
    }
    func printValue() {
        print(a,b,c)
    }
}
var c1 = C1(a: 10, b: 10, c: 10)    //Argument passed to call that takes no arguments
var c2 = C1(a: 10)                  //Argument passed to call that takes no arguments
var c3 = C1(b: 10)                  //Argument passed to call that takes no arguments
var c4 = C1(c: 10)                  //Argument passed to call that takes no arguments
var c5 = C1()
c1.printValue()     //10 10 10
c2.printValue()     //10 0 0
c3.printValue()     //0 10 0
c4.printValue()     //0 0 10
c5.printValue()     //0 0 0
Copy the code

It can be seen from the contrast between A and C. When we don’t declare an initializer. Initializers are automatically generated based on the situation.

  • If a structure declares a member variable with an initial value, multiple initializers are generated.
  • If a member variable has no initial value, only an initializer that needs to be assigned to all members is automatically generated

Conclusion: When the structure is initialized, all member variables must have values

Compare C and C1, which are basically the same structure. As you can see, if an initializer is defined, no other initializers are automatically generated.

The memory layout of the structure

struct C {
    var a:Int = 0
    var b:Int = 0
    var c:Int = 0
    func printValue() {
        print(a,b,c)
    }
}
var c1 = C(a: 10, b: 10, c: 10)
print(MemoryLayout.size(ofValue: c1))//24
Copy the code

Struct.C. nit method is called to instantiate the structure.

->  0x100003848 <+8>:   movl   $0xa, %eax
    0x10000384d <+13>:  movl   %edi, -0x1c(%rbp)
    0x100003850 <+16>:  movq   %rax, %rdi
    0x100003853 <+19>:  movq   %rsi, -0x28(%rbp)
    0x100003857 <+23>:  movq   %rax, %rsi
    0x10000385a <+26>:  movq   %rax, %rdx
    0x10000385d <+29>:  callq  0x100003b90               ; struct.C.init(a: Swift.Int, b: Swift.Int, c: Swift.Int) -> struct.C at main.swift:10
Copy the code

Structure C. three Int member variables are defined. Memorylayout. size(ofValue: c1) to obtain the result 24. So the conclusion can be drawn. The memory size of a structure is the sum of all member variables. So how is it distributed in memory.

So here we’re looking at the memory distribution through breakpoints

0x100008038 | 0A 00 00 00 00 00 00 00 0A 00 00 00 00 00 00 00 0A 00 00 00 00 00 00 00
Copy the code

24 consecutive bytes after 0x100008038 are found. Each eight bytes is a hexadecimal value 0000000A, that is, 10 in decimal. Indicates that the memory of the structure is continuously distributed on the stack. Each n bit holds the corresponding member variable value. N = The size of bytes occupied by the member variable type.

Structure assignment

What happens when we assign an existing struct variable to another struct variable and modify a member of one of the structs

struct C {
    var a:Int = 0
    var b:Int = 0
    var c:Int = 0
    func printValue() {
        print(a,b,c)
    }
}
var c1 = C(a: 10, b: 10, c: 10)
var c2 = c1
c1.a = 20
c1.printValue()	//20 10 10
c2.printValue()	//10 10 10
Copy the code

Conclusion The assignment between structures is explained. It’s a deep copy.

When a structure is assigned a value to another structure, a contiguous segment of memory is reopened and the corresponding value is put in place. When this is done, the two structures are completely independent.

We can look at their memory distribution as shown above

c1--0x100008038 | 14 00 00 00 00 00 00 00 0A 00 00 00 00 00 00 00 0A 00 00 00 00 00 00 00
c2--0x100008050 | 0A 00 00 00 00 00 00 00 0A 00 00 00 00 00 00 00 0A 00 00 00 00 00 00 00 
Copy the code

You can see that the memory distribution is exactly 24 bytes different. The structure is continuously distributed on the stack.

Structure copy on write

In Swift,String, Array, Dictionary and other structure types, in order not to waste memory, adopt copy on write operation.

That is, when the type above is assigned, if no write operation is performed. Both variables point to the same block of address. A copy operation is performed only when a write operation is performed.

Class (class)

Class initializer

Classes and structures look very similar. But the rules for class generation initializers are different.

class A {
    var a = 0
    var b = 0
}
let a = A()

class B {					//Class 'B' has no initializers
    var a : Int
    var b : Int
}
let b = B()				//'B' cannot be constructed because it has no accessible initializers

class C {
    var a : Int
    var b : Int
    init() {}			//Return from initializer without initializing all stored properties
}
let c = C()

class D {
    var a : Int
    var b : Int
    init() {
        self.a = 0
        self.b = 0
    }
}
let d = D()

class E {
    var a : Int
    var b : Int
    init(a:Int, b:Int) {
        self.a = a
        self.b = b
    }
}
let e = E(a: 10, b: 10)
Copy the code

As can be seen from the example above. If you declare a class, there are several possibilities

  • Initial values are added to member variables of the class. An initializer with no arguments is automatically generated.
  • Class member variables that have no initializers declare parameterless initializers that need to be assigned in the initializers
  • Manually declare initializers with parameters

Class memory distribution

From assembly below, you can see that __allocating_init is called when the class is initialized.

class A {
    var a = 10
    var b = 10
}
let a = A()

->  0x100003251 <+17>: movq   %rcx, %rdi
    0x100003254 <+20>: movq   %rsi, -0x18(%rbp)
    0x100003258 <+24>: callq  0x1000032a0               ; type metadata accessor for struct.A at <compiler-generated>
    0x10000325d <+29>: movq   %rax, %r13
    0x100003260 <+32>: movq   %rdx, -0x20(%rbp)
    0x100003264 <+36>: callq  0x1000036e0               ; struct.A.__allocating_init() -> struct.A at main.swift:11
Copy the code

Tracing further down,_allocating_init->_swift_allocObject -> swift_slow_alloc -> malloc

You will see that the malloc method is finally called. Note during initialization of class instance objects. A new section of memory is created in the heap space to hold the contents of the instance object. So let’s see what’s in the heap space

a | 0x10057fc20 28 82 00 00 01 00 00 00
                03 00 00 00 02 00 00 00 
                0A 00 00 00 00 00 00 00 
                0A 00 00 00 00 00 00 00
Copy the code

By looking at the memory distribution. I found two values of 0A. That’s exactly what a and B are. The first 16 bits. Store the reference count and type information of the object respectively

Class assignment

class A { var a = 10 var b = 10 func printValue() { print(a,b) } } let a = A() let b = a b.a = 20 a.printValue() //20 10  b.printValue() //20 10Copy the code

Unlike structs, class assignments are shallow copies. It’s just a copy of the heap address. Changing the value of one instance object variable causes the value of the other to change

Class and structure

Type:

  • Structure: Value type assigned to another parameter. Equal value pass. Will copy one more memory. And the two parts of the memory is completely independent. Deep copy
  • Class: Reference type assignments are passed by reference, passing only a shallow copy of the pointer

In memory:

  • Struct: N consecutive bytes on the stack. N = the size of the structure, which may exist in the stack or global area (data segment)
  • Class: holds pointer variables on the stack. The pointer points to the address of the actual class object in the heap. The class always exists in the heap. But Pointers to it may have stacks or global extents.

Initialization:

  • Struct: Just call the init method

  • Class: _allocating_init -> _swift_allocObject -> swift_slow_alloc -> malloc