前言
-
Swift 语言有两种基本的数据类型,即类(class)和结构体(struct),class 这样的概念大家不会陌生,而 struct 也并不是什么新的概念,在 Objective-C 和 C 中也有 struct,不过 Swift 将 struct 提升到一个更高更重要的层次,甚至 Swift Foundation 框架的 SDK,诸如
String,Array,Dictionary
都是基于 struct 实现的。 -
结构体是由一系列具有相同类型或不同类型的数据构成的数据集合。
-
和 class 一样,struct 也可以定义属性和方法,同样 struct 也要求完整初始化,即保证初始化过程中每一个 non-optional 属性要赋予明确的初始值。
-
结构体是值类型,并且只有在赋予变量或常量,或者被函数调用时才被赋值。
1、结构体的创建
-
结构体基本结构
struct 结构体名 { var 或 let 字段名 = 初始化值 var 或 let 字段名: 类型 }
struct Student { var age = 0 } var stu = Student() /// struct 结构体关键字 /// Student 结构体名称 /// student() 创建一个结构体变量
1.1 标准定义
-
结构体的定义
-
定义结构体字段时可以直接定义一个字段名,并且给字段名赋初始值。或者只定义一个字段名,不赋初始值,但是不赋初始值的字段在创建结构体变量时必须赋初始值。
struct Student { var name: String // 只定义一个字段名,不赋初始值 var age = 0 // 定义一个字段名,并且给字段名赋初始值 }
-
-
结构体的使用
-
创建结构体变量时,结构体的字段都必须都有初始值,否则会报错。
let s = Student(name: "appple", age: 8) // 创建结构体变量时初始化结构体的字段 print("\(s.name), \(s.age)") // 结构体类型变量值的调用
-
1.2 基本定义
-
结构体的定义
-
每个字段名都赋初始值。
struct BookInfo { var id: Int = 0 // 每个字段名都赋初始值 var name: String = "Default" var author: String = "Default" var rootType: String = "Default" }
-
-
结构体的使用
-
创建结构体变量时,结构体的字段都必须都有初始值,否则会报错。
let book = BookInfo() // 创建结构体变量时不再需要初始化结构体的字段 print("\(book.id), \(book.name), \(book.author), \(book.rootType)") // 结构体类型变量值的调用
-
2、结构体的方法
2.1 定义枚举的方法
-
枚举方法的定义
struct markStruct1 { var mark1: Int = 0 var mark2: Int = 0 var mark3: Int = 0 func sum(a: Int) -> Int { // 定义方法 return (self.mark1 + self.mark2 + self.mark3) * a } }
-
枚举方法的使用
let mark1 = markStruct1(mark1: 11, mark2: 22, mark3: 33) print(mark1.sum(a: 2)) // print 132
2.2 自定义构造方法
-
与 class 的初始化方法不同点
- struct 的 init 并没有便利初始化(convenience init)方法。
- 因为 struct 没有继承,所以 struct 的 init 也不需要 required 关键字。
-
这样对比,struct 的初始化方法比起 class 的初始化方法来说要简单的多,因为 struct 只有指定初始化,struct 只需要保证指定初始化过程中每个非可选属性都赋值,没有复杂的初始化规则和规范,struct 相比于 class 也显得更加简单,更有亲和力。
-
一旦我们自定义了初始化器,系统自动的初始化器就不起作用了,如果还需要使用到系统提供的初始化器,在我们自定义初始化器后就必须显式的定义出来。
-
可失败构造方法
struct markStruct2 { var mark1: Int var mark2: Int var mark3: Int init?(mark1: Int, mark2: Int, mark3: Int) { // 可失败(failable)的构造方法 self.mark1 = mark1 self.mark2 = mark2 self.mark3 = mark3 } }
-
普通构造方法
struct markStruct3 { var mark1: Int var mark2: Int var mark3: Int init(mark1: Int, mark2: Int, mark3: Int) { // 普通的构造方法 self.mark1 = mark1 self.mark2 = mark2 self.mark3 = mark3 } }
-
使用
let mark2 = markStruct2(mark1: 11, mark2: 22, mark3: 33) print("\(mark2?.mark1 ?? 0), \(mark2?.mark2 ?? 0), \(mark2?.mark3 ?? 0)") // print 11, 22, 33 let mark3 = markStruct3(mark1: 11, mark2: 22, mark3: 33) print("\(mark3.mark1), \(mark3.mark2), \(mark3.mark3)") // print 11, 22, 33
3、Swift 中枚举、结构体和类的比较
3.1 在 Swift 中类型引用和值引用的区别
-
对于类型引用(class reference),将变量
a
赋值给变量b
,即b = a
,这样的赋值语句仅仅将b
的指针与a
的指针一样,指向同一块内存区域,此时改变b
的值,a
也会跟着改变; -
对于值引用(value reference),赋值语句
b = a
处理的过程是开辟一个新的内存b
,将a
变量的内容拷贝后存放到内存b
,这时a
和b
完全没有关系的两个变量,对b
的改变不会影响到a
,反之亦然。 -
在 Objective-C 时代,我们对类型引用和值引用就有了一定的了解,例如在 Objective-C 中常用的
NSArray, NSDictionary, NSString, UIKit
等都是类型引用;而NSInteger, CGFloat, CGRect
等则是值引用。 -
显然,在 Objective-C 中,引用类型占据了很大的比重,现在使用 Swift 开发应用程序,开发者需要转变观念,因为 struct 在 Swift 变得越来越重要,观念的转变不仅在于多使用 struct,还要求开发者理解 struct 的原理,优点及缺点。
3.2 枚举、结构体、类的共同点
- 定义属性用于存储值,枚举只能定义计算属性
- 定义方法以提供功能
- 定义下标,以便用下标语法来访问它们的值
- 定义初始化程序,以创建它们的初始状态
- 支持扩展增加功能,来扩展它们的默认实现
- 可以遵循协议,来完成特定标准功能
3.3 枚举、结构体、类的区别
- 类是引用类型,而枚举和结构体是值类型
- 类有继承功能,而枚举和结构体没有继承的功能
3.4 类特有的功能
- 继承
- 允许类型转换
- 析构方法释放资源
- 引用计数
3.5 该如何选择
-
关于在新建一个类型时如何选择到底是使用值类型还是引用类型的问题其实在理解了两者之间的区别后是非常简单的,在这苹果官方已经做出了非常明确的指示
- 当你使用 Cocoa 框架的时候,很多 API 都要通过
NSObject
的子类使用,所以这时候必须要用到引用类型 class。 - 在其他情况下,有下面几个准则。
- 当你使用 Cocoa 框架的时候,很多 API 都要通过
-
1)什么时候该用值类型
- 要用
==
运算符来比较实例的数据时 - 你希望那个实例的拷贝能保持独立的状态时
- 数据会被多个线程使用时
- 要用
-
2)什么时候该用引用类型(class)
- 要用
==
运算符来比较实例身份的时候 - 你希望有创建一个共享的、可变对象的时候
- 要用