The article was first posted on my personal blog

preface

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

struct Point {
    var x: Int = 0
	var y: Int = 0 
}
let p1 = Point()
let p2 = Point(x: 10, y: 20)
let p3 = Point(x: 10)
let p4 = Point(y: 20)
Copy the code

But if you change it to a class, it won’t compile

class Point {
    var x: Int = 0
    var y: Int = 0
    
}
let p1 = Point()
letP2 = Point(x: 10, y: 20) Argument passed to call that takes no argumentsletP3 = Point(x: 10)// Argument passed to call that takes no argumentsletP4 = Point(y: 20) Argument passed to call that takes no argumentsCopy the code

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 exactly equivalent

struct Point {
    var x: Int = 0
	var y: Int = 0 
}
	
	
struct Point {
    var x: Int
    var y: Int
	init() { 
		x=0 
		y=0
	} 
}

Copy the code

Essential differences between structures and classes

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

Eg: We have the following struct point and class size

// class size class size {var width = 1 var height = 2} // class Point struct Point {var x = 3 var y = 4} // Var point = point () var point = point ()Copy the code

We assume that after test(), the memory address of point is 0x10000 and the memory address of point is 0x10010 can be represented by a graph

The size variable is a reference copy of the pointer 0x90000 (), and the corresponding heap space 0x90000 actually stores 1 and 2. Of course, there are 16 bytes in the front. Holds class information, and reference counts. Since Swift and OC use reference counts for memory management, the Size object uses 32 bytes

Assembly validation

The following code

unc test1Struct Point {var x = 3 var y = 4} struct Point {var x = 3 var y = 4 Var point = point () var size = size ()}test(1)Copy the code

Assembly validation validates the structure

Var point = point (

testSwift`__allocating_init() in Size #1 in test1():
    0x100001030 <+0>:  pushq  %rbp
    0x100001031 <+1>:  movq   %rsp, %rbp
    0x100001034 <+4>:  pushq  %r13
    0x100001036 <+6>:  subq   $0x18, %rsp
    0x10000103a <+10>: xorl   %eax, %eax
    0x10000103c <+12>: movl   %eax, %edi
->  0x10000103e <+14>: callq  0x100001250               ; type metadata accessor for Size #1 in testSwift.test1() -> () at <compiler-generated>
    0x100001043 <+19>: movl   $0x20, %ecx
    0x100001048 <+24>: movl   %ecx, %esi
    0x10000104a <+26>: movl   $0x7, %ecx
    0x10000104f <+31>: movl   %ecx, %edi
    0x100001051 <+33>: movq   %rdi, -0x10(%rbp)
    0x100001055 <+37>: movq   %rax, %rdi
    0x100001058 <+40>: movq   -0x10(%rbp), %rax
    0x10000105c <+44>: movq   %rdx, -0x18(%rbp)
    0x100001060 <+48>: movq   %rax, %rdx
    0x100001063 <+51>: callq  0x100005046               ; symbol stub for: swift_allocObject
    0x100001068 <+56>: movq   %rax, %r13
    0x10000106b <+59>: callq  0x100001e30               ; init() -> Size #1 in testSwift.test1() -> () in Size #1 in testSwift.test1() -> () at main.swift:15
    0x100001070 <+64>: addq   $0x18, %rsp
    0x100001074 <+68>: popq   %r13
    0x100001076 <+70>: popq   %rbp
    0x100001077 <+71>: retq   

Copy the code

from

0x10000103e <+14>: callq  0x100001250 ; type metadata accessor for Size #1 in testSwift.test1() -> () at <compiler-generated> 
Copy the code

Execute the LLDB command si to trace it


testSwift`init() in Point #1 in test1():
->  0x100001030 <+0>:  pushq  %rbp
    0x100001031 <+1>:  movq   %rsp, %rbp
    0x100001034 <+4>:  xorps  %xmm0, %xmm0
    0x100001037 <+7>:  movaps %xmm0, -0x10(%rbp)
    0x10000103b <+11>: movq   $0x3, -0x10(%rbp)
    0x100001043 <+19>: movq   $0x4, -0x8(%rbp)
    0x10000104b <+27>: movl   $0x3, %eax
    0x100001050 <+32>: movl   $0x4, %ecx
    0x100001055 <+37>: movl   %ecx, %edx
    0x100001057 <+39>: popq   %rbp
    0x100001058 <+40>: retq   

Copy the code

As you can see, the assignment directly assigns $0x3 and $0x4 to the stack space (-0x10(% RBP) and -0x8(% RBP)) without calling malloc alloc

Assembler validation validation classes

Var size = size ()

 0x100000fe0 <+0>:  pushq  %rbp
    0x100000fe1 <+1>:  movq   %rsp, %rbp
    0x100000fe4 <+4>:  pushq  %r13
    0x100000fe6 <+6>:  subq   $0x28, %rsp
    0x100000fea <+10>: movq   $0x0, -0x10(%rbp)
    0x100000ff2 <+18>: xorps  %xmm0, %xmm0
    0x100000ff5 <+21>: movaps %xmm0, -0x20(%rbp)
    0x100000ff9 <+25>: xorl   %eax, %eax
    0x100000ffb <+27>: movl   %eax, %edi
->  0x100000ffd <+29>: callq  0x100001250               ; type metadata accessor for Size #1 in testSwift.test1() -> () at <compiler-generated>
    0x100001002 <+34>: movq   %rax, %r13
    0x100001005 <+37>: movq   %rdx, -0x28(%rbp)
    0x100001009 <+41>: callq  0x100001030               ; __allocating_init() -> Size #1 in testSwift.test1() -> () in Size #1 in testSwift.test1() -> () at main.swift:15
    0x10000100e <+46>: movq   %rax, -0x10(%rbp)
    0x100001012 <+50>: callq  0x100001080               ; init() -> Point #1 in testSwift.test1() -> () in Point #1 in testSwift.test1() -> () at main.swift:21
    0x100001017 <+55>: movq   %rax, -0x20(%rbp)
    0x10000101b <+59>: movq   %rdx, -0x18(%rbp)
    0x10000101f <+63>: movq   -0x10(%rbp), %rdi
    0x100001023 <+67>: callq  0x1000050ac               ; symbol stub for: swift_release
    0x100001028 <+72>: addq   $0x28, %rsp
    0x10000102c <+76>: popq   %r13
    0x10000102e <+78>: popq   %rbp
    0x10000102f <+79>: retq   
Copy the code

Enter the

 0x100001009 <+41>: callq  0x100001030               ; __allocating_init() -> Size #1 in testSwift.test1() -> () in Size #1 in testSwift.test1() -> () at main.swift:15
Copy the code

Track all the way in and finally arrive at the position shown in the picture below

That is, it does allocate heap space, which verifies our previous conclusion

Object heap space application process

  • To create an instance object of a class in Swift, the process is as follows
Class.__allocating_init() 
libswiftCore.dylib:_swift_allocObject_ 
libswiftCore.dylib:swift_slowAlloc 
libsystem_malloc.dylib:malloc
Copy the code
  • The malloc function on Mac and iOS always allocates a multiple of 16
  • Class_getInstanceSize tells you how much memory the objects of your class need to occupy

eg:

class Point  {
    var x = 11
    var test = true
	var y = 22 
}
var p = Point() 
class_getInstanceSize(type(of: p)) // 40
class_getInstanceSize(Point.self) // 40
Copy the code

Value types

  • When a value type is assigned to a var, let, or function, it copies everything directly

  • This operation is similar to copying or paste a file to create a new copy of the file. Belong to deep copy

  • In Swift standard library, in order to improve the performance, String, Array, Dictionary, Set adopt Copy On Write technology

    • For example, a copy operation is performed only when there is a write operation
    • Swift ensures optimal performance for assignment operations of library value types, so there is no need to avoid assignment in order to ensure optimal performance
  • Suggestion: If you do not need to modify it, try to define it as let

Reference types

  • When a reference is assigned to a var, let, or function, it makes a copy of the memory address
  • Similar to making a double of a file (shortcut, link) that points to the same file. Belongs to a shallow copy

Enumerations, structures, and classes can all define methods

Functions defined in enumerations, structures, and classes are generally called methods

Class Size {var width = 10 var height = 10 funcshow() {
        print("width=\(width), height=\(height)")}}letStruct Point {var x =10 var y =10 func} struct Point {var x =10 var y =10 funcshow() {
        print("x=\(x), y=\(y)")}}letP = Point() p.show() p = Point() p.show() p = Point() p.show() p = Point() p.show() p = Point() p.show() p = Point() p.show()case a = "a"
    case b = "b"
    func show() {
        print("res is \(rawValue)")}}let g = grade.a
g.show() // res is a

Copy the code

References:

Swift official source code

From beginner to proficient in Swift programming

A gadget that snoops into memory details

More information, welcome to pay attention to the individual public number, not regularly share a variety of technical articles.