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.