In the Swift standard library, the vast majority of exposed types are structures, while enumerations and classes make up only a small portion.

First, structure

Common types such as Bool, Int, Double, String, Array, and Dictionary are all structures.

Custom structure:

struct Date {
    var year: Int;
    var month: Int;
    var day: Int;
}
var date = Date(year: 2019, month: 06, day: 02)
Copy the code

All constructs have an initializer (initializer, initializer method, constructor, constructor) automatically generated by the compiler.

Date(year: 2019, Month: 06, Day: 02) is passed all member values, which are used to initialize all members (called storage properties).

1.1. Initializer for structure

Depending on the situation, the compiler may generate multiple initializers for the structure. The purpose is to ensure that all members have initial values.

As you can see from the example above, the compiler helps generate initializers only if all storage properties have values.

Think: does the following code compile?

All options have a default valuenil, so it can compile.

1.2. Custom initializers

Once you define an initializer when defining a structure, the compiler does not automatically generate other initializers for it.

1.3. Explore the nature of the structure initializer

The following two pieces of code are equivalent:

A code:

Code 2:

After comparison, the init method in code 1 is exactly the same as in code 2. That is, the initialization of the storage property is done in the initialization constructor.

1.4. Structure’s memory structure

struct Point { var x = 10 var y = 20 var b = true } var p = Point() print(Mems.memStr(ofVal: Size) print(MemoryLayout<Point>. Stride) print(MemoryLayout<Point>. Alignment) /*  0x000000000000000a 0x0000000000000014 0x0000000000000001 17 24 8 */Copy the code

Since the storage attributes X and Y each take up 8 bytes (contiguous memory addresses), Bool takes up 1 byte in memory, so Point takes up 17 bytes in total, and since memory alignment is 8, 24 bytes in total are allocated.

Second, the class

The definition of a class is similar to that of a structure, but the compiler does not automatically generate initializers for classes that can pass in member values.

Define the class:

If the storage property has no initial value, an initializer with no parameters is not automatically generated:

If you replace the above class with a structure (structThe) type will not report an error:

2.1. Class initializer

If all the members of a class have specified initializers when they are defined, the compiler generates an initializer with no arguments for the class.

The initialization of a member is done in this initializer.

The following two pieces of code are equivalent:

class Point {
    var x: Int = 0
    var y: Int = 0
}

var p1 = Point()
Copy the code

Code 2:

class Point {
    var x: Int
    var y: Int
    init() {
        self.x = 0
        self.y = 0
    }
}

var p1 = Point()
Copy the code

The essential difference between structure and class

Structs are value types (enumerations are also value types) and classes are reference types (pointer types).

3.1. Memory analysis structures and classes

Sample code:

class Size { var width: Int = 1 var height: Int = 2 } struct Point { var x: Int = 3 var y: Int = 4} func test() {var size = size () print("class-size ", mems.memstr (ofRef: PTR (ofVal: &size)) print(mems.ptr (ofVal: &size)) print(mems.ptr (ofVal: &size)) print(mems.ptr (ofVal: &size)) Width (ofVal: & sie.width)) print(" size. Height ", mems.ptr (ofVal: & sie.width)) print(" size. &size.height) var point = point () print("struct-point object memory ", mems.memstr (ofVal: &point)) print(" mems.ptr (ofVal: &point) ") print(" mems.ptr (ofVal: &point) ", mems.ptr (ofVal: &point) &point.x)) print("struct-point.y ", mems.ptr (ofVal: &point.y))} test() Memory of the class-size object 0x00000001000092A8 0x0000000200000002 0x000000000001 0x0000000000000002 Memory address of the class-size pointer 0x00007FFEEfBff4D0 Class-size Memory address of the object 0x000000010061Fe80 Class-size. Width Memory address 0x000000010061FE90 class-size. Height Memory address 0x000000010061FE98 Struct-point object memory 0x0000000000000003 0x0000000000000004 Struct-point memory address 0x00007ffeEFbFF470 Struct-point. x memory address 0x00007ffeefbff478 */Copy the code

Sample code in memory:

After analysis, it can be seen that the data of the structure is directly stored in the stack space, and the instance of the class is pointer to the memory of the heap space, and the pointer is in the stack space. The example of the class in the above code takes 32 bytes, with the first 16 bytes storing the pointing type information and reference counting respectively, and the last 16 bytes actually storing the data. The memory occupied by the structure is equal to the sum of the memory occupied by the storage properties.

Note: In C, structures cannot define methods, but in C++ and Swift, methods can be defined in structures and classes. In a 64-bit environment, Pointers occupy 8 bytes.

Extension: The memory of value types (structures, enumerations) varies depending on where they are located. For example, define a global structure, which resides in the data segment (global area); If defined in a function, memory is stored in the stack space; If you define a structure in a class, memory follows the object in the heap space.

3.2. Compile analysis structures and classes

In Swift, to create an instance object of class, to allocate memory to the heap space, the process is as follows:

  • Class.__allocating_init()
  • libswiftCore.dylib:_swift_allocObject_
  • libswiftCore.dylib:swift_slowAlloc
  • libsystem_malloc.dylib:malloc

On Mac, the malloc function in iOS always allocates memory in multiples of 16 (for memory optimization).

Class_getInstanceSize tells you how much memory your class’s objects really use.

import Foundation class Point { var x: Int = 3 var y: Int = 4 var b: Bool = true } var p = Point() print(class_getInstanceSize(type(of: P))) print(class_getInstanceSize(point.self)) /Copy the code

Memory usage = 8(reference type information) + 8(reference count) + 8(storage attribute X) + 8(storage attribute Y) + 1(storage attribute B) = 33;

Memory allocation size = 8(pointing to type information) + 8(reference count) + 8(storage attribute x) + 8(storage attribute y) + Max(1(storage attribute B), 8(memory alignment number)) = 40;

Extension: If the underlying alloc or malloc function is called, the object is in heap space, otherwise it is in stack space.

3.2.1. Compile analysis structures

Step 1: Create structure, break point into assembly:

Step 2: Incallq... init()Function into the function implementation body (LLDB enters the function body instruction :si) :

Conclusion: RBP is a local variable, so objects created by the structure are stored on the stack.

Extension: In general, RBP is a local variable, RIP is a global variable, and RET is a function return.

3.2.2. Assembly analysis classes

Step 1: Create structure, break point into assembly:

Step 2: Incallq... __allocating_init()...Function to enter the function body:

Step 3: Incallq... swift_allocObjectFunction to enter the function body:

Step 4: Go all the way tolibswiftCore.dylib swift_allocObject:, in thecallq... swift_slowAllocAt the interrupt point of entry:

Step 5:mallocContinue into the function body:

Step 6: Finally, the object is inlibsystem_malloc.dylibLibrary executionmalloc:

After the above analysis, it is clear that objects are stored in heap space.

Extension: On Mac and iOS, objects are created by calling the libsystem_malloc.dylib dynamic library.