Swift结构体(Struct)

第一:与class的不同点

age给定默认值

/**结构体*/
struct LGTeacher {
    var age = 10
    func teacher() {
        print("teacher")
    }
}
var t = LGTeacher()

age不给定默认值

/**结构体*/
struct LGTeacher {
    var age: Int
    func teacher() {
        print("teacher")
    }
}
var t = LGTeacher(age: 10)

结论:
相对于class来说,struct会自动给定初始化方法,如果是class的情况下,会报错


struct_001.png

第二:如果我们的属性有默认的初始值,那么我们的系统就不会提供默认的初始化方法了

在SIL角度来剖析一下原理:
打开项目文件夹目录

cd /Users/zhou/Desktop/SwiftTwoPractice/SwiftTwoPractice

把 mian.swift编译成main.sil并打开(推荐使用vs code)

swiftc -emit-sil main.swift | xcrun swift-demangle >> ./main.sil && open main.sil
No application knows how to open /Users/zhou/Desktop/SwiftTwoPractice/SwiftTwoPractice/main.sil.

来看main.sil,提供了两个初始化方法

init(age: Int = 10)
init()

struct LGTeacher {
  @_hasStorage @_hasInitialValue var age: Int { get set }
  func teacher()
  init(age: Int = 10)
  init()
}

如果我自己实现了init方法

struct LGTeacher {
    var age = 10
    func teacher() {
        print("teacher")
    }
    init(age:Int) {
        self.age = age
    }
}

如果是这样的话,当前编译器就不会帮我生成init方法了,再次查看main.sil

struct LGTeacher {
  @_hasStorage @_hasInitialValue var age: Int { get set }
  func teacher()
  init(age: Int)
}

这个时候这里面就只有我们自己生成的初始化方法了

第三:结构体是值类型

先了解什么是值类型:

  • 地址存储的就是值
  • 传递的过程中相当于传递了一个副本

例子:

func test() {
    var age = 18
    var age2 = age //设置断点 ①
    age = 30 //设置断点 ②
    age2 = 45
    print("age=\(age),age2\(age2)")
}

var age = 18
意味着在栈区声明了一个地址来存储age变量,然后将18这个字面量给到当前的地址空间

在栈区创建的内存空间是由系统管理的
通过lldb调试来查看age的地址,这个地址很明显是个栈上的地址空间(高地址)

(lldb) po withUnsafeMutablePointer(to: &age){print($0)}
0x00007ffeefbff410
0 elements

然后对这个内存进行格式化输出,在这个内存地址里面直接存储的我们的值18(0x0000000000000012 )

(lldb) x/8g 0x00007ffeefbff410
0x7ffeefbff410: 0x0000000000000012 0x0000000000000000
0x7ffeefbff420: 0x00007ffeefbff440 0x0000000100002a14
0x7ffeefbff430: 0x00007ffeefbff468 0x0000000100015025
0x7ffeefbff440: 0x00007ffeefbff458 0x00007fff20393621

当放开设置的断点①,停留在断点②处的时候,将age赋值给了age2,等同于我直接把age里面的值18拿出来赋值给了age2,也就是说将18赋值给了age2

同样适用lldb调试看结果:

(lldb) po withUnsafeMutablePointer(to: &age2){print($0)}
0x00007ffeefbff408
0 elements

(lldb) po withUnsafeMutablePointer(to: &age){print($0)}
0x00007ffeefbff410
0 elements

可以看出,age与age2这两个地址只差了8字节大小,栈空间地址分配的过程中是从高到低的,这也可以认证age是存储在栈上的
然后我在格式过输出age与age2的值:

(lldb) x/8g 0x00007ffeefbff408
0x7ffeefbff408: 0x0000000000000000 0x0000000000000012
0x7ffeefbff418: 0x0000000000000000 0x00007ffeefbff440
0x7ffeefbff428: 0x0000000100002a14 0x00007ffeefbff468
0x7ffeefbff438: 0x0000000100015025 0x00007ffeefbff458
(lldb) x/8g 0x00007ffeefbff410
0x7ffeefbff410: 0x0000000000000012 0x0000000000000000
0x7ffeefbff420: 0x00007ffeefbff440 0x0000000100002a14
0x7ffeefbff430: 0x00007ffeefbff468 0x0000000100015025
0x7ffeefbff440: 0x00007ffeefbff458 0x00007fff20393621
(lldb) 

这个值也是一样的,在修改的过程中,修改的是当前独立地址里面的内存的值,也就意味着,在这个过程中,可以说age变量是一个值类型
同样的,看struct的例子:

struct LGTeacher {
    var age: Int = 18
    var age2: Int = 20
}

//结构体再赋值的过程中并不会共享状态
var t = LGTeacher()
print("end")

通过lldb调试查看:

(lldb) po t
▿ LGTeacher
  - age : 18
  - age2 : 20

(lldb) po withUnsafeMutablePointer(to: &t){print($0)}
0x00000001000081a0
0 elements

(lldb) x/8g 0x00000001000081a0
0x1000081a0: 0x0000000000000012 0x0000000000000014
0x1000081b0: 0x0000000000000000 0x0000000000000000
0x1000081c0: 0x0000000000000000 0x0000000000000000
0x1000081d0: 0x0000000000000000 0x0000000000000000

可以看出,这个结构体的地址0x00000001000081a0中,直接存储的就是0x0000000000000012、0x0000000000000014,所以认证了结构体是值类型

注意点:尽量避免在值类型中包含引用类型

推荐阅读更多精彩内容