最近在学习,顺便总结和记录一下
结构体和类
要说结构体和类就需要先说说值类型和引用类型了
Swift的类型分为值类型和引用类型两种
- 值类型: 在传递和赋值时进行复制,如struct,enum。所有内建类型(包括Int,Bool,String, Array, Dictionary)(OC和Swift区别之一)
- 引用类型: 只会使用引用对象的一个“指向” ,如class
类与结构体的主要相同点
- 定义属性用于存储值。
- 定义函数以提供功能。
- 定义下标,以便用下标语法来访问它们的值。
- 通过扩展来扩展它们的默认实现。
- 利用协议来完成特定标准功能。
类与结构体的主要不同点
- 类:引用类型。 结构体: 值类型,在设计结构体时,我们可以要求编译器保证不可变性。
- 内存的管理方式有所不同,类的实例只能通过引用来间接地访问。类能有很多个持有者.需要自己管理其引用计数、引用值得变化。 结构体可以被直接持有及访问,不会被引用,但是会被复制。也就是说,结构体的持有者是唯一的。
- 类:可以继承,结构体(以及枚举): 不能被继承的。
结构体的一些说明
- 结构体只有一个持有者,所以它不可能造成引用循环。而对于类和函数这样的引用类型,我们需要特别小心,避免造成引用循环的问题。
- 值总是需要复制这件事情听来可能有点低效,不过,编译器可以帮助我们进行优化,以避免很 多不必要的复制操作。因为结构体非常基础和简单,所以这是可能的。结构体复制的时候发生 的是按照字节进行的浅复制。除非结构体中含有类,否则复制时都不需要考虑其中属性的引用 计数。当使用 let 来声明结构体时,编译器可以确定之后这个结构体的任何一个字节都不会被改变。
- 如果一个结构体只由其他结构体组成,那编译器可以确保不可变性。同样地,当使用结构体时,编译器也可以生成非常快的代码。举个例子,对一个只含有结构体的数组进行操作的效率,通常要比对一个含有对象的数组进行操作的效率高得多。这是因为结构体通常要更直接:值是直接存储在数组的内存中的。而对象的数组中包含的只是对象的引用。最后,在很多情况下,编译器可以将结构体放到栈上,而不用放在堆里。
几个相关的名词
值类型
每个实例都保留了一分独有的数据拷贝,一般以结构体 (struct)、枚举(enum) 或者元组(tuple)的形式出现。
值语义
结构体只有一个持有者。比如,当我们将结构体变量传递给一个函数时,函数将接收到结构体的复制,它也只能改变它自己的这份复制。这叫做值语义,有时候也被叫做 复制语义。
引用类型
每个实例共享同一份数据来源,一般以类(class)的形式出现。
引用语义
而对于对象来说,它们是通过传递引用来工作的,因此类对象会拥有很多持有者, 这被叫做引用语义
下面用代码来验证一下值类型和引用类型
首先定义一个打印内存地址的函数
// 打印对象内存地址
func printAddress(values: AnyObject...) {
for value in values {
print(Unmanaged.passUnretained(value).toOpaque())
}
print("-----------------------------------------")
}
然后定义一个类和一个结构体
class ObjectTest: NSObject {
var t1 = 0
}
struct StructTest {
var t1 = 0
}
给类和结构体分别赋值,打印下结果
let objectTest = ObjectTest()
let objectTest1 = objectTest
objectTest1.t1 = 1
print(objectTest.t1)
print(objectTest1.t1)
let structTest = StructTest(t1: 0)
var structTest1 = structTest
structTest1.t1 = 100
print(structTest.t1)
print(structTest1.t1)
1
1
0
100
给其中一个类objectTest1的t1赋值,然后objectTest的t1随之发生改变,但是结构体structTest1只改变了当前自己的t1,而structTest的t1并没有发生改变。上面说了类和结构体分别是引用类型和值类型,也就是说引用类型和值类型对应发生上面的情况,值类型每个实例保留了一分独有的数据拷贝,而每个实例共享同一份数据来源
验证, 打印结果
printAddress(values: objectTest, objectTest1)
printAddress(values: structTest as AnyObject, structTest1 as AnyObject)
0x000060400001be60
0x000060400001be60
-----------------------------------------
0x0000604000446000
0x0000604000446030
-----------------------------------------
结论
值类型复制的时候,两者的内存地址并不一样。相当于创造了一个完全独立的实例,这个实例保有属于自己的独有数据,数据不会受到其他实例的数据变化影响。值类型就好像身份证复印件一样,复印出来之后,修改原件上面的内容,复印件上的内容不会变。
引用类型复制的时候,因为两者的内存地址是一样的。相当于默默地创造了一个共享的实例分身,两者是共用一套数据。因此修改其中任何一个实例的数据,也会影响到另外那个。
网友评论