iOS开发之Swift篇(8)—— 类和结构体

版本

Xcode 11.3.1
Swift 5.1.3

Swift 中结构体和类的功能很相近,本文中所讨论的大部分功能都可以用在结构体或者类上,因此将两者放在同一篇章里。

结构体和类对比

Swift 中结构体和类有很多共同点。两者都可以:

  • 定义属性用于存储值
  • 定义方法用于提供功能
  • 定义下标操作用于通过下标语法访问它们的值
  • 定义构造器用于设置初始值
  • 通过扩展以增加默认实现之外的功能
  • 遵循协议以提供某种标准功能

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

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

选结构体还是类?
类支持的附加功能是以增加复杂性为代价的。按照通用的准则,当符合一条或多条以下条件时,请考虑构建结构体:

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

举例来说,以下情境中适合使用结构体:

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

类型定义 & 创建实例

// 定义结构体
struct Characteristic {
    
    
    var height = 0
    var weight = 0
}

// 定义类
class People {
    
    
    var name: String?
    var age = 0
    var characteristic = Characteristic()
}

// 实例化
// 类的实例化
let people = People()
// 结构体的实例化
var characteristic1 = Characteristic()
// 结构体的另一种方法实例化 (成员逐一构造器,类没有这种方法)
let characteristic2 = Characteristic(height: 180, weight: 200)

people.name = "小明"
people.age = 18
people.characteristic.height = 180
people.characteristic.weight = 150
print("\(people.name!)的年龄是\(people.age)岁,身高是\(people.characteristic.height)厘米,体重是\(people.characteristic.weight)斤")
// 小明的年龄是18岁,身高是180厘米,体重是150斤

characteristic1.height = 100;
characteristic1.weight = 50;
print("特征1 身高为\(characteristic1.height), 体重为\(characteristic1.weight)")
// 特征1 身高为100, 体重为50

print("特征2 身高为\(characteristic2.height), 体重为\(characteristic2.weight)")
// 特征2 身高为180, 体重为200

结构体是值类型

值类型是这样一种类型,当它被赋值给一个变量、常量或者被传递给一个函数的时候,其值会被拷贝。
例如,声明一个特征3并用特征2的值赋值给前者,然后改变特征3的height和weight,发现特征2里的值没有跟随改变:

var characteristic3 = characteristic2
characteristic3.height = 200
characteristic3.weight = 300
print("特征2 身高为\(characteristic2.height), 体重为\(characteristic2.weight)")
print("特征3 身高为\(characteristic3.height), 体重为\(characteristic3.weight)")
// 特征2 身高为180, 体重为200
// 特征3 身高为200, 体重为300

实际上,Swift 中所有的基本类型:整数(integer)、浮点数(floating-point number)、布尔值(boolean)、字符串(string)、数组(array)和字典(dictionary),都是值类型,其底层也是使用结构体实现的。

类是引用类型

与值类型不同,引用类型在被赋予到一个变量、常量或者被传递到一个函数时,其值不会被拷贝。因此,使用的是已存在实例的引用,而不是其拷贝。
例如,声明一个people1并把people赋值给他,然后改变people1的名字,发现原来的people也跟着改变了

let people1 = people
people1.name = "小红"
print("people 名称为\(people.name!)")
print("people1 名称为\(people1.name!)")
// people 名称为小红
// people1 名称为小红

需要注意的是 people1 被声明为常量而不是变量。然而你依然可以改变 people1.name 等属性,这是因为 people1这个常量的值并未改变。它们并不“存储”这个 People 实例,而仅仅是对 People 实例的引用。所以,改变的是底层 People 实例的 name 属性,而不是指向 People 的常量引用的值。

恒等运算符

因为类是引用类型,所以多个常量和变量可能在幕后同时引用同一个类实例。(对于结构体和枚举来说,这并不成立。因为它们作为值类型,在被赋予到常量、变量或者传递到函数时,其值总是会被拷贝。)
判定两个常量或者变量是否引用同一个类实例有时很有用。为了达到这个目的,Swift 提供了两个恒等运算符:

  • 相同(===)
  • 不相同(!==)
if people1 === people {
    
    
    print("people1 和 people 引用同一个实例")
}
// 打印 people1 和 people 引用同一个实例

指针

如果你有 C,C++ 或者 Objective-C 语言的经验,那么你也许会知道这些语言使用指针来引用内存中的地址。Swift 中引用了某个引用类型实例的常量或变量,与 C 语言中的指针类似,不过它并不直接指向某个内存地址,也不要求你使用星号(*)来表明你在创建一个引用。相反,Swift 中引用的定义方式与其它的常量或变量的一样。

猜你喜欢

转载自blog.csdn.net/u012078168/article/details/104350158