天天看点

➽08结构体与类

比较结构体与类

Swift中的结构和类有许多共同之处。两者都可以:

● 定义属性以存储值

● 定义方法以提供功能

● 定义子下标以使用下标语法提供对其值的访问

● 定义初始化器以设置其初始状态

● 扩展其功能以超出默认实现

● 符合协议以提供某种标准功能

类具有结构所不具备的其他功能:

● 继承使一个类能够继承另一个类的特征。

● 类型转换使您能够在运行时检查和解释类实例的类型。

● Deinitializers使类的实例能够释放已分配的任何资源。

● 引用计数允许对类实例进行多个引用。

类支持的额外功能是以增加复杂性为代价的。作为一般指导原则,我们更喜欢结构,因为它们更容易推理,并且在适当或必要时使用类。实际上,这意味着您定义的大多数自定义数据类型都是结构和枚举。

定义语法

struct SomeStructure {
    // structure definition goes here
}
class SomeClass {
    // class definition goes here
}
           
struct Resolution {
    var width = 0
    var height = 0
}
class VideoMode {
    var resolution = Resolution()
    var interlaced = false
    var frameRate = 0.0
    var name: String?
}
           

name属性会自动指定一个默认值

nil

或“no name value”,因为它是可选类型。

结构体和类实例

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

这将创建类或结构的新实例,并将所有属性初始化为其默认值。

访问和修改属性

print("The width of someResolution is \(someResolution.width)")
// Prints "The width of someResolution is 0"

print("The width of someVideoMode is \(someVideoMode.resolution.width)")
// Prints "The width of someVideoMode is 0"

someVideoMode.resolution.width = 1280
print("The width of someVideoMode is now \(someVideoMode.resolution.width)")
// Prints "The width of someVideoMode is now 1280"
           

结构体成员逐一初始化

与结构体不同,类实例不接收默认的成员初始值设定项。

结构体与枚举都是值类型

值类型是这么一种类型:其值在赋值给变量或常量或传递给函数时被复制。

事实上,Swift整数、浮点数、布尔值、字符串、数组和字典中的所有基本类型都是值类型,并在幕后作为结构体实现。

所有结构和枚举都是Swift中的值类型。这意味着您创建的任何结构和枚举实例以及它们作为属性的任何值类型在代码中传递时都会被复制。

由标准库定义的集合(如数组、字典和字符串)使用优化来降低复制的性能成本。这些集合不是立即创建副本,而是在原始实例和任何副本之间共享存储元素的内存。如果修改了集合的一个副本,则将在修改之前复制元素。您在代码中看到的行为总是好像立即发生了复制。
let hd = Resolution(width: 1920, height: 1080)
var cinema = hd
           

因为Resolution是一个结构,所以将创建现有实例的副本,并将此新副本指定给cinema。尽管hd和cinema现在拥有相同的宽度和高度,但它们在幕后却是完全不同的两个实例。下面示例也佐证了这一点:

cinema.width = 2048

print("cinema is now \(cinema.width) pixels wide")
// Prints "cinema is now 2048 pixels wide"

print("hd is still \(hd.width) pixels wide")
// Prints "hd is still 1920 pixels wide"
           

对于枚举,行为也是一样的:

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)")
// Prints "The current direction is north"
// Prints "The remembered direction is west"
           

类是引用类型

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

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

let alsoTenEighty = tenEighty
alsoTenEighty.frameRate = 30.0

print("The frameRate property of tenEighty is now \(tenEighty.frameRate)")
// Prints "The frameRate property of tenEighty is now 30.0"
           

ID验证运算符

因为类是引用类型,所以多个常量和变量可以在幕后引用同一个类的单个实例。(对于结构和枚举,情况并非如此,因为当它们被赋给常量或变量或传递给函数时,它们总是被复制。)

有时,找出两个常量或变量是否引用了一个类的完全相同的实例是很有用的。为了实现这一点,Swift提供了两个标识运算符:

===

!==

使用这些运算符检查两个常量或变量是否引用同一个实例:

if tenEighty === alsoTenEighty {
    print("tenEighty and alsoTenEighty refer to the same VideoMode instance.")
}
// Prints "tenEighty and alsoTenEighty refer to the same VideoMode instance."
           

指针

如果您有C、C++或Object-C的经验,您可能知道这些语言使用指针来引用内存中的地址。引用某个引用类型实例的Swift常量或变量类似于C中的指针,但不是指向内存中地址的直接指针,也不需要写星号(*)来表示正在创建引用。相反,这些引用的定义与Swift中的任何其他常量或变量相同。标准库提供了指针和缓冲区类型,如果需要直接与指针交互,可以使用这些类型。