Swift进阶03: 值类型 & 引用类型

2021-01-14  本文已影响0人  黑白森林无间道

值类型

值类型是一种当它被指定到常量或者变量,或者被传递给函数时会被拷贝的类型。
Swift 中所有的基本类型——整数浮点数布尔量字符串数组字典——都是值类型,并且都以结构体的形式在后台实现。
Swift 中所有的结构体枚举都是值类型

内存的五大分区

内存地址

什么是值类型

通过下列代码来分析值类型

func test(){
    //栈区声明一个地址,用来存储age变量
    var age = 18
    //传递的值
    var age2 = age
    //age、age2是修改独立内存中的值
    age = 30
    age2 = 45
    
    print("age=\(age),age2=\(age2)")
}
test()

从例子中可以得出,age存储在栈区

值类型 值类型

从上面可以得出,age就是值类型
值类型的特点

结构体

结构体的初始化

定义一个结构体

// ***** 未定义init方法 *****
struct HTTeacher {
    var age: Int = 18
    func teacher() {
        print("teacher")
    }
}

// ***** 自定义init方法 *****
struct HTTeacher {
    var age: Int = 18
    func teacher() {
        print("teacher")
    }
    init(age: Int) {
        self.age = age
    }
}

var t = HTTeacher(age: 20)

结构体的SIL分析

结构体是值类型

定义一个结构体,并进行分析

struct HTTeacher {
    var age: Int = 18
    var age2: Int = 20
}
var t = HTTeacher()
print("end")
结构体

问题:此时将t赋值给t1,如果修改了t1,t会发生改变吗?

t2 被赋予 t 的当前值,存储在 t中的值就被拷贝给了新的 t2实例。这最终的结果是两个完全不同的实例,它们只是碰巧包含了相同的数字值。由于它们是完全不同的实例, t2age被设置 30并不影响 tage存储的值。

结构体

SIL分析

生成SIL文件swiftc -emit-sil main.swift | xcrun swift-demangle >> ./main.sil && open main.sil
SIL文件中,我们查看结构体的初始化方法,可以发现只有init,而没有malloc,在其中看不到任何关于堆区的分配

结构体

总结

mutating 方法异变

结构体枚举值类型。默认情况下,值类型属性不能被自身的实例方法修改。

image sil
从上图中可以看出,push函数除了item,还有一个默认参数self,这两个参数都是let类型,是不允许修改的
struct HTStack {
    var items: [Int] = []
    mutating func push(_ item: Int) {
        items.append(item)
    }
}

查看SIL文件,找到 push函数,发现与之前有所不同。添加 mutating后,本质是给值类型函数添加了 inout关键字,相当于在值传递过程中,传递的是引用(即地址)

sil

inout关键字

一般情况下,在函数、方法的声明中,参数都是默认不可变的,如果想要直接修改,需要给参数加上 inout关键字。

未加inout inout关键字

总结

引用类型-类

类的定义

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

class HTTeacher1 {
    var age: Int = 18
    var age2: Int = 20
}

//结构体:值类型
var t = HTTeahcer()
// 类: 引用类型
// t1 存储在全局区
var t1 = HTTeacher1()

打印t、t1, 从下图中可以发现,t中存储的是值,t1内存空间中存储的是地址

值类型&引用类型

获取t1变量的地址,并查看内存情况

引用类型

引用类型的特点

问题1:此时将t1赋值给t2,如果修改了t2,会导致t1修改吗?

引用类型

问题2:如果结构体中包含类对象,此时如果修改t1中的实例对象属性,t会改变吗?
代码如下:

struct HTTeahcer {
    var age: Int = 18
    var age2: Int = 20
    var teacher: HTTeacher1 = HTTeacher1()
}
class HTTeacher1 {
    var age: Int = 18
    var age2: Int = 20
}

var t = HTTeahcer()

var t1 = t
t1.teacher.age = 30

print(t.teacher.age)    // 30
print(t1.teacher.age)   // 30

虽然在结构体中是值传递,但是对于teacher,由于是引用类型,所以传递的依然是地址

注意:在编写代码过程中,应该尽量避免值类型包含引用类型

SIL分析

查看当前的SIL文件,尽管HTTeacher1是放在值类型中的,在传递的过程中,不管是传递还是赋值,teacher都是按照引用计数开始管理的

SIL

通过po CFGetRetainCount(t.teacher)查看,teacher的引用计数为3

SIL分析

主要是因为:

SIL分析

总结

通过上述 LLDB查看结构体和类的内存模型,有以下结论:

上一篇 下一篇

猜你喜欢

热点阅读