Swift 编程语言基础数据类型与语法详解

Viewed 0

基础内容

Swift 提供了许多基础数据类型,包括用于整型值的 Int、浮点数值的 DoubleFloat、布尔量值的 Bool 以及字符串值的 String。此外,Swift 也为三个主要的集合类型提供了高效的版本:ArraySetDictionary

Swift 使用变量来存储和调用值,并通过变量名进行区分。同时,Swift 也大量采用了值不可变的变量,称为常量。当处理的值不需要更改时,使用常量可以让代码更加安全并清晰地表达意图。

除了熟悉的类型,Swift 还引入了高级类型,例如元组。元组允许创建和传递一组数据,并可以在一个函数中以单个复合值的形式返回多个值。Swift 还增加了可选项,用于处理值可能缺失的情况。可选项表示两种可能:要么有一个特定类型的值,要么根本没有值。

Swift 是一门类型安全的语言,它帮助开发者明确代码可以操作值的类型。例如,如果一段代码预期得到一个 String,类型安全会阻止不小心传入一个 Int。同样,它也会阻止给一个要求非可选 String 的代码传入可选 String。这种限制有助于在开发过程中更早地发现并修复错误。

常量和变量

常量和变量将一个名字(例如 maximumNumberOfLoginAttemptswelcomeMessage)与一个特定类型的值(例如数字 10 或字符串 "Hello")关联起来。常量的值一旦设置就不能再更改,而变量可以在将来被设置为不同的值。

声明常量和变量

常量和变量必须在使用前声明,使用关键字 let 声明常量,使用关键字 var 声明变量。例如,以下代码记录用户登录次数:

let maximumNumberOfLoginAttempts = 10
var currentLoginAttempt = 0

这段代码声明了一个名为 maximumNumberOfLoginAttempts 的新常量,并设置其值为 10;同时声明了一个名为 currentLoginAttempt 的新变量,并赋予初始值 0。在这个例子中,登录次数的最大值被声明为常量,因为它永远不会更改;当前尝试登录的次数被声明为变量,因为该值在每次登录尝试失败后会递增。

如果存储的值不会改变,请使用 let 关键字将其声明为常量;只有储存会改变的值时才使用变量。声明时可以立即赋值,也可以在后面分配初始值,只要确保在第一次读取之前它有一个值即可。例如:

var environment = "development"
let maximumNumberOfLoginAttempts: Int
// maximumNumberOfLoginAttempts 目前还没有值

if environment == "development" {
    maximumNumberOfLoginAttempts = 100
} else {
    maximumNumberOfLoginAttempts = 10
}
// 现在 maximumNumberOfLoginAttempts 有值了,可以读取

你可以在一行中声明多个变量或常量,用逗号分隔:var x = 0.0, y = 0.0, z = 0.0

类型标注

可以在声明变量或常量时提供类型标注,以明确其能够存储的值的类型。添加类型标注的方法是在变量或常量名后加一个冒号、空格,然后跟上类型名称。例如,声明一个名为 welcomeMessage 的变量,类型为 String

var welcomeMessage: String

这表示 welcomeMessage 变量可以存储任何 String 值。你可以在一行中定义多个相关变量为相同类型,只需在最后的变量名后加上类型标注:var red, green, blue: Double

实际上,经常不需要使用类型标注。如果在定义常量或变量时设定了初始值,Swift 几乎总是能推断出它的类型。在上面的 welcomeMessage 例子中,由于没有提供初始值,所以使用了类型标注来明确类型。

命名常量和变量

常量和变量的名字几乎可以使用任何字符,包括 Unicode 字符,例如 let π = 3.14159let 你好 = "你好世界"let 🐶🐮 = "dogcow"。但名字不能包含空白字符、数学符号、箭头、保留的 Unicode 码位、连线和制表符,也不能以数字开头,尽管数字可以用在名字的其他位置。

一旦声明了确定类型的常量或变量,就不能使用相同的名字再次声明,也不能让它改存其他类型的值。常量和变量之间也不能互换。如果需要使用 Swift 保留的关键字作为名称,可以用反引号包围它,但应尽量避免。

对于变量,可以将其值更改为其他相同类型的值。例如:

var friendlyWelcome = "Hello!"
friendlyWelcome = "Bonjour!"
// friendlyWelcome 现在是 "Bonjour!"

不同于变量,常量的值一旦设定就不能再改变,尝试更改会导致编译时错误。

输出常量和变量

可以使用 print(_:separator:terminator:) 函数来打印当前常量和变量中的值。这是一个全局函数,用于输出一个或多个值。在 Xcode 中,输出会显示在控制台面板上。separatorterminator 参数有默认值,调用时可以忽略。默认情况下,函数通过在行末尾添加换行符来结束输出;要输出不带换行符的值,可以传递一个空字符串作为终止符。

Swift 使用字符串插值的方式将常量名或变量名作为占位符加入到更长的字符串中,然后用当前值替换这些占位符。将常量或变量名放入圆括号中并在括号前使用反斜杠转义:print("The current value of friendlyWelcome is \(friendlyWelcome)")

注释

注释用于将不需要执行的文本放入代码中,作为标记或提醒。Swift 编译器在编译时会忽略注释。单行注释以两个斜杠开头://。多行注释以一个斜杠加星号开头:/*,以星号加斜杠结尾:*/。Swift 中的多行注释可以嵌套在其他多行注释中,这使得注释掉一大段包含多行注释的代码块变得方便。

分号

与许多其他语言不同,Swift 不要求每句代码结尾写分号,但如果你想写也可以。如果在一行里写多句代码,分号是必需的:let cat = "🐱"; print(cat)

整数

整数是没有小数部分的数字,如 42-23。整数可以是有符号(正、零或负)或无符号(正数或零)。Swift 提供了 8、16、32 和 64 位编码的有符号和无符号整数,类型命名与 C 相似,例如 UInt8Int32

整数范围

可以通过 minmax 属性访问每个整数类型的最小值和最大值:

let minValue = UInt8.min // 最小值是 0,类型是 UInt8
let maxValue = UInt8.max // 最大值是 255,类型是 UInt8

这些属性的值都是自适应大小的数字类型,可以在表达式中与其他相同类型的值一起使用。

Int

在大多数情况下,不需要为整数设置特定长度。Swift 提供了一个额外的整数类型 Int,其长度与当前平台的原生字相同:在 32 位平台上,Int 的长度与 Int32 相同;在 64 位平台上,与 Int64 相同。除非需要操作特定长度的整数,否则尽量使用 Int 作为整数的值类型,这能提高代码的统一性和兼容性。

UInt

Swift 也提供了一种无符号整数类型 UInt,其长度与当前平台的原生字相同:在 32 位平台上与 UInt32 相同,在 64 位平台上与 UInt64 相同。只有在确实需要存储与平台原生字长度相同的无符号整数时才使用 UInt;其他情况下,即使知道值都是非负的,也推荐使用 Int,以统一类型并避免转换问题。

浮点数

浮点数是有小数的数字,如 3.141590.1-273.15。浮点类型能表示比整数类型更大范围的值,可以存储比 Int 类型更大或更小的数字。Swift 提供了两种有符号浮点数类型:Double 代表 64 位浮点数,Float 代表 32 位浮点数。Double 有至少 15 位数字的精度,而 Float 的精度只有 6 位。在两种类型都可以的情况下,推荐使用 Double

类型安全和类型推断

Swift 是类型安全的语言,这意味着代码可以清楚地知道能处理哪些类型的值。例如,如果一部分代码期望获得 String,就不能错误地传给它一个 Int。类型安全在编译时进行类型检查,任何不匹配的类型都会被标记为错误,帮助在开发阶段早期发现并修复问题。

类型推断允许 Swift 在编译阶段自动推断出合适的类型,而无需为每个值显式声明类型。通过检查赋给变量的值,Swift 可以推断出类型。例如:

let meaningOfLife = 42 // meaningOfLife 被推断为 Int 类型
let pi = 3.14159 // pi 被推断为 Double 类型

当在表达式中结合整数和浮点数时,Double 会被推断出来:let anotherPi = 3 + 0.14159 中,anotherPi 被推断为 Double

数值型字面量

整数型字面量可以写作十进制(无前缀)、二进制(前缀 0b)、八进制(前缀 0o)或十六进制(前缀 0x)。例如,以下所有整数字面量的十进制值都是 17

let decimalInteger = 17
let binaryInteger = 0b10001
let octalInteger = 0o21
let hexadecimalInteger = 0x11

浮点字面量可以是十进制(无前缀)或十六进制(前缀 0x)。小数点两边必须有至少一个数字。十进制浮点字面量有一个可选的指数,用 e 表示;十六进制浮点字面量必须有指数,用 p 表示。例如,以下浮点字面量的值都是 12.1875

let decimalDouble = 12.1875
let exponentDouble = 1.21875e1
let hexadecimalDouble = 0xC.3p0

数值型字面量可以添加额外的零或下划线来增加可读性,而不影响值:let paddedDouble = 000123.456let oneMillion = 1_000_000

数值类型转换

通常使用 Int 类型来处理整数,即使知道值是非负的,这可以确保代码直接复用并符合类型推断。只有在特殊情况下,如处理外部明确长度的数据或优化性能时,才使用指定长度的类型。

整数转换

不同整数类型存储的数字范围不同。例如,Int8 可存储 -128127UInt8 可存储 0255。如果数字超出范围,编译时会报错。要将一种数字类型转换为另一种,需要用当前值初始化一个期望的类型。例如:

let twoThousand: UInt16 = 2_000
let one: UInt8 = 1
let twoThousandAndOne = twoThousand + UInt16(one)

这里,UInt16(one) 创建了一个新的 UInt16 值,使得加法可以进行。

整数和浮点数转换

整数和浮点数之间的转换必须显式指定类型:

let three = 3
let pointOneFourOneFiveNine = 0.14159
let pi = Double(three) + pointOneFourOneFiveNine // pi 等于 3.14159,类型为 Double

浮点数转换为整数时,数值会被截断。例如,Int(pi) 会得到 3

类型别名

类型别名为已存在的类型定义一个新的可选名字,使用 typealias 关键字。例如,typealias AudioSample = UInt16。一旦创建了别名,就可以在任何使用原始名字的地方使用它。

布尔值

Swift 有一个基础的布尔类型 Bool,其值只能是 truefalse。例如:

let orangesAreOrange = true
let turnipsAreDelicious = false

类型推断会将使用布尔值初始化的常量或变量推断为 Bool 类型。布尔值在条件语句中非常有用,如 if 语句。Swift 的类型安全机制阻止用非布尔值替换 Bool,例如 if i 会导致编译错误,而 if i == 1 则是可行的,因为 i == 1 的结果是 Bool 类型。

元组

元组将多个值合并成单一的复合值,元组内的值可以是任何类型,不必相同。例如,(404, "Not Found") 是一个描述 HTTP 状态代码的元组,类型为 (Int, String)

可以将元组的内容分解成单独的常量或变量:

let (statusCode, statusMessage) = http404Error
print("The status code is \(statusCode)")

如果只需要部分数据,可以用下划线忽略不需要的部分。也可以通过索引访问元素:http404Error.0。在定义元组时,可以给单个元素命名,然后通过名字访问:let http200Status = (statusCode: 200, description: "OK")

元组在临时值组合中很有用,但不适合创建复杂的数据结构;对于复杂情况,应使用类或结构体。

可选项

可选项用于处理值可能缺失的情况,表示两种可能:要么有值,可以展开获取;要么根本没有值。例如,将 String 转换为 Int 可能失败,因此返回一个可选的 Int,写作 Int?

nil

可以通过给可选变量赋值 nil 来将其设置为没有值。如果定义可选变量时没有提供默认值,变量会自动设置为 nil。使用 if 语句比较 nil 来判断可选项是否包含值。例如:

if convertedNumber != nil {
    print("convertedNumber contains some integer value.")
}

非可选常量或变量不能使用 nil。将可选和非可选项分开,可以明确标记哪些信息可以缺失,从而更容易处理缺失值。

可选项绑定

使用可选项绑定来判断可选项是否包含值,如果包含就把值赋给一个临时常量或变量。例如:

if let actualNumber = Int(possibleNumber) {
    print("'\(possibleNumber)' has an integer value of \(actualNumber)")
} else {
    print("'\(possibleNumber)' could not be converted to an integer")
}

如果不需要引用原本的可选项,可以使用相同的名字。简便写法是只写要展开的常量或变量名,新的展开常量或变量会隐式使用相同名字。常量和变量都可以使用可选项绑定。

可以在同一个 if 语句中包含多个可选项绑定,用逗号分隔。如果任一绑定结果为 nil 或布尔值为 false,整个 if 判断视为 false

提供备选值

处理缺失值的另一种方法是使用空值合并操作符 ?? 提供默认值。如果操作符左边的可选值不是 nil,则使用该值;否则使用右边的值。例如:

let name: String? = nil
let greeting = "Hello, " + (name ?? "friend") + "!"

强制展开

nil 表示不可恢复的故障时,可以通过在可选项名称后加感叹号 ! 来强制展开值。强制展开非空值时,结果是其展开值;强制展开 nil 值会引发运行时错误。实际上,!fatalError(_:file:line:) 的简写。

隐式展开可选项

隐式展开可选项在声明时就能确认其中有值,因此可以像非可选值那样使用,无需每次访问都展开。通过在类型后加叹号定义,如 String!。如果尝试访问值为 nil 的隐式展开可选项,会触发运行时错误。可以像检查普通可选项一样检查它是否为 nil,或使用可选项绑定。

错误处理

错误处理机制允许为错误状况负责,判断错误原因并将错误传递到程序的其他地方。函数通过 throws 关键字表明可能抛出错误,调用时需要在表达式前加 try。错误被传递到 catch 分句处理。do 语句创建新范围,允许错误传递到多个 catch 分句。

断言和先决条件

断言和先决条件用于检测运行时事件,确保在执行后续代码前必要条件是满足的。如果条件为 true,代码继续执行;如果为 false,程序终止。断言只在 debug 构建时检查,而先决条件在 debug 和生产构建中都生效。使用它们可以强制数据和状态正确,帮助调试和减小损失。

使用断言进行调试

断言在运行时检查逻辑条件是否为 true,使用 assert(_:_:file:line:) 函数。如果条件为 false,显示信息并终止应用。例如:

let age = -3
assert(age >= 0, "A person's age cannot be less than zero")

如果代码已检查条件,可以使用 assertionFailure(_:file:line:) 标明断言失败。

强制先决条件

在条件可能为假但必须为真才能继续执行的地方使用先决条件,例如检测下标越界。使用 precondition(_:_:file:line:) 函数。如果条件为 false,显示信息。可以调用 preconditionFailure(_:file:line:) 标明错误发生。注意,在不检查模式编译时,先决条件不会检查;而 fatalError(_:file:line:) 函数总会终止执行,可用于标记未实现的功能。

0 Answers