基础概述

Swift是iOS,macOS,watchOS和tvOS应用程序开发的新编程语言。

Swift提供了所有C和Objective-C基本类型的自己的版本,包括IntDoubleFloatBoolString。还提供了三大集合类型(ArraySetDictionary)的强大版本。

Swift使用变量来存储并通过识别名称引用值。还广泛使用常量,比C中的常量更加强大。在使用不需要更改的值时,Swift推荐使用常量,使得代码在意图上更安全和更清晰。

除熟悉的类型外,Swift引入了元组。元组可以创建和传递分组值,也可以在函数中作为复合值返回多个值。

Swift引入可选类型,以处理缺失值。表示“有一个值,等于x”或“根本没有值”。可选类型类似在Objective-C中的nil指针,nil只能在类中使用,但可选类型适用于任何类型。

Swift是一种类型安全语言,这意味着该语言可以帮助开发者清楚地了解自己正在使用的值的类型。如果代码中需要String类型,输入安全会避免你错误地传给Int类型。类型安全可帮助开发者尽可能早地捕获并修复错误。


常量和变量

常量值在设置后不可更改,变量值可随意修改。

声明常量和变量

常量和变量在使用前必须声明。常量用let关键字声明,变量用var。下面例子使用常量和变量跟踪用户进行的登陆尝试次数:

let maximumNumberOfLoginAttempts = 10
var currentLoginAttempt = 0

以上代码表明:声明一个常量maximumNumberOfLoginAttempts,并赋值10;声明一个变量currentLoginAttempt,并赋值0。

在此示例中,允许登陆尝试的最大次数由于不再更改被声明为常量,当前登陆尝试次数被声明为变量,因为每次失败的登陆尝试之后必须增加该值。

可以在单行声明多个常量或变量,用逗号隔开:

var x = 0.0, y = 0.0, z = 0.0

注意

若代码中的值不会更改,始终使用let将其声明为常量。仅使用变量来存储需要更改的值。

类型标注

在声明常量或变量时可提供类型标注,以清楚和强调值的类型。

var welcomeMessage: String

代码表明:声明一个String类型的welcomeMessage变量。

现在变量welcomeMessage可以设置为任意String类型的值。

可以在单行上定义多个相同类型的变量,以逗号隔开:

var red, green, blue: Double

注意

在代码中使用类型标注并不多见。Swift的类型推断机制可以在常量或变量初始化后根据初始值推断出该常量或变量的类型。由于上述例子中,变量welcomeMessage未提供初始值,需要使用类型标注指定变量类型,而不是通过类型推断。

常量和变量命名

常量和变量名称可包含任意字符,包括Unicode字符:

let π = 3.14159
let 你好 = "你好世界"
let ?? = "dogcow"

常量和变量名称不能包含空格字符、数学符号、箭头、专用Unicode字符,且不能以数字开头。

注意

除非别无选择,常量和变量名称不要使用Swift关键字。

变量赋值后可更改为同类型的其他值:

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

常量一旦初始化后不可更改,否则会导致编译错误:

let languageName = "Swift"
languageName = "Swift++"
// 编译错误: languageName cannot be changed.

打印常量和变量

使用**print(_:separator:terminator:)**函数可以打印常量或变量值:

print(friendlyWelcome)
// 打印 "Bonjour!"

使用字符串插值将常量或变量作为占位符:

print("The current value of friendlyWelcome is \(friendlyWelcome)")
// 打印 "The current value of friendlyWelcome is Bonjour!"


注释

单行注释:

// 这是单行注释.

多行注释:

/* 这是
多行注释 */

嵌套多行注释:

/* 这是多行注释的第一行.
 /* 这是嵌套注释的第二行. */
 这是多行注释的最后一行. */


分号

Swift语句后无需使用分号(使用分号也不报错)。若在单行写多条语句,则必须加上分号:

let cat = "?"; print(cat)
// 打印 "?"


整数

Swift提供8位、16位、32位和64位的有符号和无符号整数。

整数范围

使用整数的minmax属性可获取整数值的最小值和最大值:

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

Int

在大多数情况下,不需要使用特定大小的整数类型,使用Int足够。Int类型大小与当前平台有关:

  • 在32位系统中,Int类型等同Int32
  • 在64位系统中,Int类型等同Int64

UInt

  • 在32位系统中,UIn*类型等同UInt32
  • 在64位系统中,UInt类型等同UInt64


浮点数

Swift提供两个带符号的浮点数类型:

  • Double表示64位浮点数,精确度为15位小数
  • Float表示32位浮点数,精确度为6位小数

注意

DoubleFloat类型都适合时,优先使用Double


类型安全与类型推断

当声明带有初始值的变量或常量时,类型推断特别有用。

以下代码常量meaningOfLife在没有标注类型的情况下被编译器推断为Int类型:

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

浮点数都被推断为Double类型:

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

推断混合数字类型运算结果:

let anotherPi = 3 + 0.14159
// anotherPi 被推断为Double类型


数字字面量

整数字面量的进制表示:

  • 十进制:前缀
  • 二进制:0b 前缀
  • 八进制:0o 前缀
  • 十六进制:0x 前缀

以下整数均表示数字17

let decimalInteger = 17
let binaryInteger = 0b10001       // 二进制
let octalInteger = 0o21           // 八进制
let hexadecimalInteger = 0x11     // 十六进制

浮点数的指数和进制表示:

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

数字可添加额外的格式使其易读:

let paddedDouble = 000123.456
let oneMillion = 1_000_000
let justOverOneMillion = 1_000_000.000_000_1


数字类型转换

通常情况下使用默认整数类型可立即在代码中相互操作并直接推断类型,除非在特殊情况下(如特定大小的数据、内存考虑、性能优化)使用明确大小的类型。

Swift没有类似C的隐式类型转换,只有强制类型转换

整数类型转换

不同大小的整数类型联合操作时需要使用类型转换:

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

整数和浮点数转换

整数和浮点数转换必须为显式转换:

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

整数类型可以使用浮点数初始化:

let integerPi = Int(pi)


类型别名

使用类型别名替代现有类型名称,关键字typealias

typealias AudioSample = UInt16

定义了类型别名后,可在任何可能的地方使用别名:

var maxAmplitudeFound = AudioSample.min


布尔类型

布尔类型表示逻辑,Swift提供了两种布尔常量值,truefalse

let orangesAreOrange = true
let turnipsAreDelicious = false

布尔类型用于条件语句作逻辑判断:

if turnipsAreDelicious {
    print("Mmm, tasty turnips!")
} else {
    print("Eww, turnips are horrible.")
}
// 打印 "Eww, turnips are horrible."

类型安全可防止非布尔值替换为Bool

let i = 1
if i {
    // 引发编译错误
}

只有Bool类型值才能在条件语句中使用,这样可避免上述意外错误,并确保代码的意图是种清晰:

let i = 1
if i == 1 {
    // 成功编译
}

元组

元组将做个值组合成单个复合值,且可以是任何类型,不必是相同类型。

以下示例是一个描述HTTP状态码的元组:

let http404Error = (404, "Not Found")
// http404Error 为 (Int, String)类型

元组解包

let (statusCode, statusMessage) = http404Error
print("状态码:\(statusCode)")
// 打印 "状态码: 404"
print("状态信息: \(statusMessage)")
// 打印 "状态信息: Not Found"

当需要忽略某个元组解包的值时,使用**(_)**:

let (justTheStatusCode, _) = http404Error
print("状态码: \(justTheStatusCode)")
// 打印 "状态码: 404"

元组下标取值:

print("状态码: \(http404Error.0)")
// 打印 "状态码: 404"
print("状态信息: \(http404Error.1)")
// 打印 "状态信息: Not Found"

可在定义元组时给元素命名:

let http200Status = (statusCode: 200, description: "OK")

使用元素名称取值:

print("状态码:\(http200Status.statusCode)")
// 打印 "状态码:200"
print("状态信息: \(http200Status.description)")
// 打印 "状态信息: OK"

可在函数中返回元组表示返回多个值

注意

元组只用于临时存储多值,不适合创建较复杂的数据结构。


可选类型

在值可能不存在的情况下定义为可选类型

注意

在Objective-C中使用nil表示对象不存在,但是该nil仅适用于对象,对于普通类型通常使用特殊值表示没有值。而Swift的可选类型不需要使用特殊常量,可表示任何类型的值缺失。

例如,Int类型有一个初始化器,可将String值转换为Int值。但是,并不是所有任何字符串都能转换为整数。字符串“123”可转换为数值123,但字符串“hello world”无法转换为数值。

let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
// convertedNumber 被推断为 "Int?"类型, 或 "optional Int"类型

nil

将可选类型值赋值为nil表示值缺失:

var serverResponseCode: Int? = 404
// serverResponseCode 包含一个实际的Int值 404
serverResponseCode = nil
// serverResponseCode 不包含任何值

当定义一个可选类型变量没有给定默认值时,变量会自动设置为nil

var surveyAnswer: String?
// surveyAnswer 被自动设置为nil

if语句和强制解包

在使用可选类型值时,需确保该值不为nil。使用if语句将可选类型值与nil比较来判断该值是否包含真实值:

if convertedNumber != nil {
    print("convertedNumber 包含整数值.")
}
// Prints "convertedNumber 包含整数值."

一旦你确保可选类型确实包含值,可以通过强制解包获取该值,即在可选类型名末尾添加感叹号。

if convertedNumber != nil {
    print("convertedNumber 有整数值: \(convertedNumber!).")
}
// 打印 "convertedNumber 有整数值: 123."

注意

在对可选类型使用强制解包前,必须确保其包含非nil值,否则会抛出运行错误!

可选绑定

使用可选绑定可以确定可选类型是否包含非nil值,并将该值用作临时常量或变量。

语法如下:

if let constantName = someOptional {
    statements
}

使用可选绑定,而不是强制解包来重写上述possibleNumber例子:

if let actualNumber = Int(possibleNumber) {
    print("\"\(possibleNumber)\" 有整数值: \(actualNumber)")
} else {
    print("\"\(possibleNumber)\" 不能转换为整数")
}
// 打印 ""123" 有整数值: 123"

上述代码表示:若Int(possibleNumber)包含值,将其保存在actualNumber常量中。

可选绑定可使用常量或变量,上述例子中可书写var actualNumber将可选值存储在变量中,并在语句块中改变该值。

可在单条if语句中包含多个可选绑定,以逗号分隔:

if let firstNumber = Int("4"), let secondNumber = Int("42"), firstNumber < secondNumber && secondNumber < 100 {
    print("\(firstNumber) < \(secondNumber) < 100")
}
// 打印 "4 < 42 < 100"
 
if let firstNumber = Int("4") {
    if let secondNumber = Int("42") {
        if firstNumber < secondNumber && secondNumber < 100 {
            print("\(firstNumber) < \(secondNumber) < 100")
        }
    }
}
// 打印 "4 < 42 < 100"

隐式解包的可选类型

隐式解包的可选类型为使用时可自动解包的可选类型,而不用每次都需使用强制解包或可选绑定。当隐式解包的可选类型值为nil,编译器依然会抛出错误。起主要应用在类的属性初始化,

声明强制解包的可选类型时,只需在类型后添加感叹号而不是问号:

let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // 使用强制解包
 
let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // 不需要强制解包

也可以将隐式解包的可选类型当作普通的可选类型,使用if语句或可选绑定:

if assumedString != nil {
    print(assumedString)
}
// 打印 "An implicitly unwrapped optional string."

if let definiteString = assumedString {
    print(definiteString)
}
// 打印 "An implicitly unwrapped optional string."

注意

切勿将可能变为nil值声明为隐式解包的可选类型,若需在变量生命周期内检查nil值,请始终使用常规可选类型。