类和结构体

  • 结构体作为一种通用而又灵活的结构,是构建代码的基础;
  • 可以使用定义常量、变量和函数的语法,为你的结构体和类定义属性、添加方法
  • Swift 只需在单一的文件中定义一个结构体或者类,系统将会自动生成面向其它代码的外部接口

注:通常一个的实例被称为对象。然而相比其他语言,Swift 中结构体和类的功能更加相近,本文中所讨论的大部分功能都可以用在结构体或者类上。因此,这里会使用实例这个更通用的术语。


结构体和类对比

功能 结构体
定义属性用于存储值 支持 支持
定义方法用于提供功能 支持 支持
定义构造器用于设置初始值 支持 支持
通过扩展以增加默认实现之外的功能 支持 支持
遵循协议以提供某种标准功能 支持 支持
定义下标操作用于通过下标语法访问它们的值 支持 支持
继承允许一个类继承另一个类的特征(继承) 支持 不支持
类型转换允许在运行时检查和解释一个类实例的类型 支持 不支持
析构器允许一个类实例释放任何其所被分配的资源(资源释放) 支持 不支持
引用计数允许对一个类的多次引用(计数引用) 支持 不支持

注:类支持的附加功能是以增加复杂性为代价的。作为一般准则,优先使用结构体,因为它们更容易理解,仅在适当或必要时才使用类,即需要发挥类的特性时使用。


类型定义的语法

1
2
3
4
5
6
struct SomeStructure {
// 在这里定义结构体
}
class SomeClass {
// 在这里定义类
}

注:

  • 每当你定义一个新的结构体或者类时,你都是定义了一个新的 Swift 类型。请使用 UpperCamelCase 这种方式来命名类型(如这里的 SomeClassSomeStructure),以便符合标准 Swift 类型的大写命名风格(如 StringIntBool)。
  • 请使用 lowerCamelCase 这种方式来命名属性和方法(如 frameRateincrementCount),以便和类型名区分
定义结构体和定义类的示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 定义一个商品模型
struct ImageInfo {
var url = ""
var size = 0
var height = 0
var width = 0
}

class GoodsMode {
var logo = ImageInfo()
var name = ""
var price = 0.0
var des: String?
}

  • 上面的示例中定义了一个名为 ImageInfo 的结构体,用来描述图片的信息;
  • 这个结构体包含了名为 url 、size、height 、width 四个存储属性;
  • 当属性被初始化为整数 0 的时候,它们会被推断为 Int 类型;初始化为空字符串,则被推断为String
  • 示例还定义了一个名为 GoodsMode 的类,用来描述商品的基本属性;
  • 第一个, logo,被初始化为一个新的 ImageInfo 结构体的实例,属性类型被推断为 ImageInfo
  • GoodsMode 实例同时还会初始化其它三个属性,其中des为可选String型,它会被自动赋予一个默认值 nil

结构体和类的实例

ImageInfo 结构体和 GoodsMode 类的定义仅描述了什么是 ImageInfoGoosdMode。它们并没有描述一个特定的图片(logo)或者商品(goods mode);创建结构体和类实例的语法非常相似:

1
2
3
4
//结构体和类都使用构造器语法来创建新的实例,构造器语法的最简单形式是在结构体或者类的类型名称后跟随一对空括号
let logo = ImageInfo()
let goods = GoodsMode()
//通过这种方式所创建的类或者结构体实例,其属性均会被初始化为默认值

属性访问

通过使用点语法访问实例的属性。其语法规则是,实例名后面紧跟属性名,两者以点号(.)分隔,不带空格

1
2
3
4
5
print("The width of ImageInfo is \(logo.width)")
//// 打印 "The width of ImageInfo is 0"

//访问子属性
print("The width of someGoodsMode is \(goods.logo.width)")

使用点语法为可变属性赋值:

1
2
goods.logo.width = 1280
print("The width of someGoodsMode is now \(goods.logo.width)")

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

所有结构体都有一个自动生成的成员逐一构造器,用于初始化新结构体实例中成员的属性,如下:

1
let logo = ImageInfo(url:"xxxxxxxxx",size:10234, width: 640, height: 480)

注:与结构体不同,类实例没有默认的成员逐一构造器,具体构造技术,参考构造过程


结构体和枚举是值类型

  • 值类型: 当它被赋值给一个变量、常量或者被传递给一个函数的时候,其值会被拷贝
  • Swift 中所有的基本类型:整数(integer)、浮点数(floating-point number)、布尔值(boolean)、字符串(string)、数组(array)和字典(dictionary),都是值类型,其底层也是使用结构体实现的
  • Swift 中所有的结构体和枚举类型都是值类型:这意味着它们的实例,以及实例中所包含的任何值类型的属性,在代码中传递的时候都会被复制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
enum CompassPoint {
case north, south, east, west
mutating func turnNorth() {
self = .north
}
}
var currentDirection = CompassPoint.west
let rememberedDirection = currentDirection
currentDirection.turnNorth()

print("The current direction is \(currentDirection)")
print("The remembered direction is \(rememberedDirection)")
// 打印 "The current direction is north"
// 打印 "The remembered direction is west"

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


类是引用类型

  • 引用类型:在被赋予到一个变量、常量或者被传递到一个函数时,其值不会被拷贝,因此,使用的是已存在实例的引用,而不是其拷贝。

恒等运算符

  • 因为类是引用类型,所以多个常量和变量可能在幕后同时引用同一个类实例.对于结构体和枚举来说,这并不成立。因为它们作为值类型,在被赋予到常量、变量或者传递到函数时,其值总是会被拷贝
  • 判定两个常量或者变量是否引用同一个类实例有时很有用
    1. 相同(===
    2. 不相同(!==

请注意,“相同”(用三个等号表示,===)与“等于”(用两个等号表示,==)的不同。“相同”表示两个类类型(class type)的常量或者变量引用同一个类实例。“等于”表示两个实例的值“相等”或“等价”,判定时要遵照设计者定义的评判标准,当在定义你的自定义结构体和类的时候,你有义务来决定判定两个实例“相等”的标准。在章节 等价操作符 中将会详细介绍实现自定义 == 和 != 运算符的流程