跳转至

类和结构体

类和结构体

目录


1.类和结构体对比

Swift中类和结构体有很多共同点

  • 定义属性用于存储值
  • 定义方法用于提供功能
  • 定义附属脚本用于访问值
  • 定义构造器用于生成初始化值
  • 通过扩展以增加默认实现的功能
  • 符合协议以对某类提供标准功能

与结构体相比,类还有如下的附加功能。

  • 继承允许一个雷继承另一个类的特征
  • 类型转换允许在运行时检查和解释一个类实例的类型
  • 解构器允许一个类实例释放任何其所被分配的资源
  • 引用计数器允许对一个类的多次引用

2.定义

通过关键字class和struct来分别表示类和结构体,并在一对大括号中定义它们的具体内容。

定义一个名为Resolution的结构体,用来描述一个显示器的分辨率。这个结构体包含两个名为width和height的存储属性。

1
2
3
4
struct Resolution{
    var width = 0
    var height = 0
}

定义一个名为VideoMode的类,用来描述一个视频显示器的特定模式。这个类包含了四个存储属性变量。第一个是分辨率。

1
2
3
4
5
6
class VideoMode {
    var resolution = Resolution()
    var interlaced = false
    var frameRate = 0.0
    var name: String?
}

3.类和结构体实例

let someResolution = Resolution()
let someVideoMode = VideoMode()

4.属性访问

通过使用点语法,你可以访问实例中所含有的属性。其语法规则是,实例名后面紧跟属性名,两者通过点号(.)连接:

println("The width of someResolution is \(someResolution.width)")
// 输出 "The width of someResolution is 0"

你也可以访问子属性,如VideoMode中Resolution属性的width属性:

println("The width of someVideoMode is \(someVideoMode.resolution.width)")
// 输出 "The width of someVideoMode is 0"

你也可以使用点语法为属性变量赋值:

1
2
3
someVideoMode.resolution.width = 1280
println("The width of someVideoMode is now \(someVideoMode.resolution.width)")
// 输出 "The width of someVideoMode is now 1280"

5.结构体类型的成员逐一构造器

所有结构体都有一个自动生成的成员**逐一构造器**,用于初始化新结构体实例中成员的属性。新实例中各个属性的初始值可以通过属性的名称传递到成员逐一构造器之中:

let vga = Resolution(width:640, height: 480)

与结构体不同,类实例没有默认的成员逐一构造器。

6.结构体和枚举是值类型

 let hd = Resolution(width: 1920, height: 1080)

var cinema = hd

cinema.width = 2048

println("cinema is now  \(cinema.width) pixels wide")
// 输出 "cinema is now 2048 pixels wide"
println("hd is still \(hd.width    ) pixels wide")
// 输出 "hd is still 1920 pixels wide"

将hd赋值给cinema的时候,实际上是将hd中所存储的值进行拷贝。然后将拷贝的数据存储到新的cinema实例中。结果就是两个完全独立的实例碰巧包含有相同的数值。由于两者相互独立,因此将cinema的width修改为2048不会影响hd中的宽。

enum CompassPoint {
    case North, South, East, West
}
var currentDirection = CompassPoint.West
let rememberedDirection = currentDirection
currentDirection = .East
if rememberDirection == .West {
    println("The remembered direction is still .West")
}
// 输出 "The remembered direction is still .West"

上例中rememberedDirection被赋予了currentDirection的值(value),实际上它被赋予的是值(value)的一个拷贝。赋值过程结束后再修改currentDirection的值并不影响rememberedDirection所储存的原始值(value)的拷贝。

7.类是引用类型

let tenEighty = VideoMode()
tenEighty.resolution = hd
tenEighty.interlaced = true
tenEighty.name = "1080i"
tenEighty.frameRate = 25.0

let alsoTenEighty = tenEighty
alsoTenEighty.frameRate = 30.0

println("The frameRate property of tenEighty is now \(tenEighty.frameRate)")
// 输出 "The frameRate property of theEighty is now 30.0"

因为类是引用类型,有可能有多个常量和变量在后台同时引用某一个类的实例。Swift内奸了两个恒等运算符

  • 等价于 (===)
  • 不等价于 (!==)

以下是运用这两个运算符检测两个常量或者变脸是否引用同一个实例。

1
2
3
4
if tenEighty === alsoTenTighty {
    println("tenTighty and alsoTenEighty refer to the same Resolution instance.")
}
//输出 "tenEighty and alsoTenEighty refer to the same Resolution instance."

8.类和结构体的选择

在你的代码中,你可以使用类和结构体来定义你的自定义数据类型。

然而,结构体实例总是通过值传递,类实例总是通过引用传递。这意味两者适用不同的任务。当你在考虑一个工程项目的数据构造和功能的时候,你需要决定每个数据构造是定义成类还是结构体。

按照通用的准则,当符合一条或多条以下条件时,请考虑构建结构体:

  • 结构体的主要目的是用来封装少量相关简单数据值。
  • 有理由预计一个结构体实例在赋值或传递时,封装的数据将会被拷贝而不是被引用。
  • 任何在结构体中储存的值类型属性,也将会被拷贝,而不是被引用。
  • 结构体不需要去继承另一个已存在类型的属性或者行为。

合适的结构体候选者包括:

  • 几何形状的大小,封装一个width属性和height属性,两者均为Double类型。
  • 一定范围内的路径,封装一个start属性和length属性,两者均为Int类型。
  • 三维坐标系内一点,封装x,y和z属性,三者均为Double类型。

在所有其它案例中,定义一个类,生成一个它的实例,并通过引用来管理和传递。实际中,这意味着绝大部分的自定义数据构造都应该是类,而非结构体。

9.集合(Collection)类型的赋值和拷贝行为

Swift 中字符串(String),数组(Array)和字典(Dictionary)类型均以结构体的形式实现。这意味着String,Array,Dictionary类型数据被赋值给新的常量(或变量),或者被传入函数(或方法)中时,它们的值会发生拷贝行为(值传递方式)。

评论