类型别名:
你可以使用 typealias 关键字来定义类型别名
当你想要给现有类型起一个更有意义的名字时,类型别名非常有用
typealias AudioSample = UInt16
var maxAmplitudeFound = AudioSample.min
AudioSample 被定义为 UInt16 的一个别名。因为它是别名,AudioSample.min 实际上是 UInt16.min,所以会给 maxAmplitudeFound 赋一个初值 0
元组:
元组(tuples)把多个值组合成一个复合值。元组内的值可以是任意类型,并不要求是相同类型
(404, "Not Found") 是一个描述 HTTP 状态码(HTTP status code)的元组。HTTP 状态码是当你请求网页的时候 web 服务器返回的一个特殊值。如果你请求的网页不存在就会返回一个 404 Not Found 状态码
let heep404Error = (404, "Not Found")
(404, "Not Found") 元组把一个 Int 值和一个 String 值组合起来表示 HTTP 状态码的两个部分:一个数字和一个人类可读的描述。这个元组可以被描述为“一个类型为 (Int, String) 的元组
你可以将一个元组的内容分解(decompose)成单独的常量和变量
let (statusCode, statusMessage) = http404Error
print("the statusCode is \(statusCode)")
print("the statusMessage is \(statusMessage)")
如果你只需要一部分元组值,分解的时候可以把要忽略的部分用下划线(_)标记
let (justTheStatusCode, _) = http404Error
print("The status code is \(justTheStatusCode)")
此外,你还可以通过下标来访问元组中的单个元素,下标从零开始
print("The status code is \(http404Error.0)")// 输出“The status code is 404”print("The status message is \(http404Error.1)")// 输出“The status message is Not Found”
你可以在定义元组的时候给单个元素命名
let http200Status = (statusCode: 200,description: "OK")
给元组中的元素命名后,你可以通过名字来获取这些元素的值
print("The status code is \(http200Status.statusCode)")// 输出“The status code is 200”print("The status message is \(http200Status.description)")// 输出“The status message is OK”
可选类型:
使用可选类型(optionals)来处理值可能缺失的情况。可选类型表示两种可能:
或者有值, 你可以解析可选类型访问这个值, 或者根本没有值
下面的例子使用这种构造器来尝试将一个 String 转换成 Int
let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
因为该构造器可能会失败,所以它返回一个可选类型(optional)Int,而不是一个 Int。一个可选的 Int 被写作 Int? 而不是 Int。问号暗示包含的值是可选类型,也就是说可能包含 Int 值也可能不包含值
你可以给可选变量赋值为 nil 来表示它没有值
var serverResponseCode: Int? = 404
serverResponseCode = nil
nil 不能用于非可选的常量和变量。如果你的代码中有常量或者变量需要处理值缺失的情况,请把它们声明成对应的可选类型
如果你声明一个可选常量或者变量但是没有赋值,它们会自动被设置为 nil
var surveyAnswer: String?
Swift 的 nil 和 Objective-C 中的 nil 并不一样。在 Objective-C 中,nil 是一个指向不存在对象的指针。在 Swift 中,nil 不是指针——它是一个确定的值,用来表示值缺失。任何类型的可选状态都可以被设置为 nil,不只是对象类型
如果可选类型有值,它将不等于 nil
if convertedNumber != nil {
print("convertedNumber contains some integer value.")
}// 输出“convertedNumber contains some integer value.”
当你确定可选类型确实包含值之后,你可以在可选的名字后面加一个感叹号(!)来获取值。这个惊叹号表示“我知道这个可选有值,请使用它。”这被称为可选值的强制解析
if convertedNumber != nil {
print("convertedNumber has an integer value of \(convertedNumber!).")
}// 输出“convertedNumber has an integer value of 123.”
使用 ! 来获取一个不存在的可选值会导致运行时错误。使用 ! 来强制解析值之前,一定要确定可选包含一个非 nil 的值
可选绑定:
使用可选绑定(optional binding)来判断可选类型是否包含值,如果包含就把值赋给一个临时常量或者变量。可选绑定可以用在 if 和 while 语句中,这条语句不仅可以用来判断可选类型中是否有值,同时可以将可选类型中的值赋给一个常量或者变量
if let actualNumber = Int(possibleNumber) {
print("\'\(possibleNumber)\' has an integer value of \(actualNumber)")
} else {
print("\'\(possibleNumber)\' could not be converted to an integer")
}// 输出“'123' has an integer value of 123”
这段代码可以被理解为:
“如果 Int(possibleNumber) 返回的可选 Int 包含一个值,创建一个叫做 actualNumber 的新常量并将可选包含的值赋给它。”
如果转换成功,actualNumber 常量可以在 if 语句的第一个分支中使用。它已经被可选类型 包含的 值初始化过,所以不需要再使用 ! 后缀来获取它的值。在这个例子中,actualNumber 只被用来输出转换结果
你可以包含多个可选绑定或多个布尔条件在一个 if 语句中,只要使用逗号分开就行。只要有任意一个可选绑定的值为 nil,或者任意一个布尔条件为 false,则整个 if 条件判断为 false,这时你就需要使用嵌套 if 条件语句来处理
if let firstNumber = Int("4"), let secondNumber = Int("42"), firstNumber < secondNumber && secondNumber < 100{
print("\(firstNumber) < \(secondNumber) < 100")
}
if let firstNumber = Int("4") {
if let secondNumber = Int("42") {
if firstNumber < secondNumber && secondNumber < 100 {
print("\(firstNumber) < \(secondNumber) < 100")
}
}
}
在 if 条件语句中使用常量和变量来创建一个可选绑定,仅在 if 语句的句中(body)中才能获取到值。相反,在 guard 语句中使用常量和变量来创建一个可选绑定,仅在 guard 语句外且在语句后才能获取到值
隐式解析可选类型:
如上所述,可选类型暗示了常量或者变量可以“没有值”。可选可以通过 if 语句来判断是否有值,如果有值的话可以通过可选绑定来解析值。
有时候在程序架构中,第一次被赋值之后,可以确定一个可选类型总会有值。在这种情况下,每次都要判断和解析可选值是非常低效的,因为可以确定它总会有值
这种类型的可选状态被定义为隐式解析可选类型(implicitly unwrapped optionals)。把想要用作可选的类型的后面的问号(String?)改成感叹号(String!)来声明一个隐式解析可选类型
当可选类型被第一次赋值之后就可以确定之后一直有值的时候,隐式解析可选类型非常有用。隐式解析可选类型主要被用在 Swift 中类的构造过程中
一个隐式解析可选类型其实就是一个普通的可选类型,但是可以被当做非可选类型来使用,并不需要每次都使用解析来获取可选值。下面的例子展示了可选类型 String 和隐式解析可选类型 String 之间的区别
let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // 需要感叹号来获取值
let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // 不需要感叹号
你可以把隐式解析可选类型当做一个可以自动解析的可选类型。你要做的只是声明的时候把感叹号放到类型的结尾,而不是每次取值的可选名字的结尾
如果你在隐式解析可选类型没有值的时候尝试取值,会触发运行时错误。和你在没有值的普通可选类型后面加一个惊叹号一样
你仍然可以把隐式解析可选类型当做普通可选类型来判断它是否包含值
if assumedString != nil {
print(assumedString!)
}// 输出“An implicitly unwrapped optional string.”
你也可以在可选绑定中使用隐式解析可选类型来检查并解析它的值
if let definiteString = assumedString {
print(definiteString)
}// 输出“An implicitly unwrapped optional string.”
如果一个变量之后可能变成 nil 的话请不要使用隐式解析可选类型。如果你需要在变量的生命周期中判断是否是 nil 的话,请使用普通可选类型
错误处理:
你可以使用 错误处理(error handling) 来应对程序执行中可能会遇到的错误条件。相对于可选中运用值的存在与缺失来表达函数的成功与失败,错误处理可以推断失败的原因,并传播至程序的其他部分
func canThrowAnError() throws {
// 这个函数有可能抛出错误
}
一个函数可以通过在声明中添加 throws 关键词来抛出错误消息。当你的函数能抛出错误消息时,你应该在表达式中前置 try 关键词
do {
try canThrowAnError()// 没有错误消息抛出
} catch {
//有一个错误消息抛出}
一个 do 语句创建了一个新的包含作用域,使得错误能被传播到一个或多个 catch 从句。
这里有一个错误处理如何用来应对不同错误条件的例子
funcmakeASandwich() throws {
// ...}do {
try makeASandwich()
eatASandwich()
} catch SandwichError.outOfCleanDishes {
washDishes()
} catch SandwichError.missingIngredients(let ingredients) {
buyGroceries(ingredients)
}
在此例中,makeASandwich()(做一个三明治)函数会抛出一个错误消息如果没有干净的盘子或者某个原料缺失。因为 makeASandwich() 抛出错误,函数调用被包裹在 try 表达式中。将函数包裹在一个 do 语句中,任何被抛出的错误会被传播到提供的 catch 从句中。
如果没有错误被抛出,eatASandwich() 函数会被调用。如果一个匹配 SandwichError.outOfCleanDishes 的错误被抛出,washDishes() 函数会被调用。如果一个匹配 SandwichError.missingIngredients 的错误被抛出,buyGroceries(_:) 函数会被调用,并且使用 catch 所捕捉到的关联值 [String] 作为参数
断言和先决条件:
断言和先决条件是在运行时所做的检查。你可以用他们来检查在执行后续代码之前是否一个必要的条件已经被满足了。如果断言或者先决条件中的布尔条件评估的结果为 true(真),则代码像往常一样继续执行。如果布尔条件评估结果为 false(假),程序的当前状态是无效的,则代码执行结束,应用程序中止
你使用断言和先决条件来表达你所做的假设和你在编码时候的期望。你可以将这些包含在你的代码中。断言帮助你在开发阶段找到错误和不正确的假设,先决条件帮助你在生产环境中探测到存在的问题
使用断言和先决条件不是一个能够避免出现程序出现无效状态的编码方法。然而,如果一个无效状态程序产生了,断言和先决条件可以强制检查你的数据和程序状态,使得你的程序可预测的中止(译者:不是系统强制的,被动的中止),并帮助使这个问题更容易调试。一旦探测到无效的状态,执行则被中止,防止无效的状态导致的进一步对于系统的伤害
断言和先决条件的不同点是,他们什么时候进行状态检测:断言仅在调试环境运行,而先决条件则在调试环境和生产环境中运行。在生产环境中,断言的条件将不会进行评估。这个意味着你可以使用很多断言在你的开发阶段,但是这些断言在生产环境中不会产生任何影响
使用断言进行调试:
你可以调用 Swift 标准库的 assert(_:_:file:line:) 函数来写一个断言。向这个函数传入一个结果为 true 或者 false 的表达式以及一条信息,当表达式的结果为 false 的时候这条信息会被显示
let age = -3
assert(age >= 0, "A person's age cannot be less than zero")// 因为 age < 0,所以断言会触发
在这个例子中,只有 age >= 0 为 true 时,即 age 的值非负的时候,代码才会继续执行。如果 age 的值是负数,就像代码中那样,age >= 0 为 false,断言被触发,终止应用
如果代码已经检查了条件,你可以使用 assertionFailure(_:file:line:) 函数来表明断言失败了,例如:
if age > 10 {
print("You can ride the roller-coaster or the ferris wheel.")
} else if age > 0 {
print("You can ride the ferris wheel.")
} else {
assertionFailure("A person's age can't be less than zero.")
}
强制执行先决条件:
当一个条件可能为假,但是继续执行代码要求条件必须为真的时候,需要使用先决条件。例如使用先决条件来检查是否下标越界,或者来检查是否将一个正确的参数传给函数。
你可以使用全局 precondition(_:_:file:line:) 函数来写一个先决条件。向这个函数传入一个结果为 true 或者 false 的表达式以及一条信息,当表达式的结果为 false 的时候这条信息会被显示
// 在一个下标的实现里...precondition(index >0,"Index must be greater than zero.")
你可以调用preconditionFailure(_:file:line:) 方法来表明出现了一个错误,例如,switch 进入了 default 分支,但是所有的有效值应该被任意一个其他分支(非 default 分支)处理
赋值运算符:
与 C 语言和 Objective-C 不同,Swift 的赋值操作并不返回任何值。所以下面语句是无效的
let x = 5, y = 5;
if x = y{
//此句错误, 因为x=y并不返回任何值}
通过将 if x = y 标记为无效语句,Swift 能帮你避免把 (==)错写成(=)这类错误的出现
求余运算符:
在对负数 b 求余时,b 的符号会被忽略。这意味着 a % b 和 a % -b 的结果是相同的
let c = 9 % 4
let d = 9 % -4结果是相同的, 余数都是1
比较运算符:
Swift 也提供恒等(===)和不恒等(!==)这两个比较符来判断两个对象是否引用同一个对象实例
(1, "zebra") < (2, "apple") // true,因为 1 小于 2
(3, "apple") < (3, "bird") // true,因为 3 等于 3,但是 apple 小于 bird
(4, "dog") == (4, "dog") // true,因为 4 等于 4,dog 等于 dog
("blue", -1) < ("purple", 1) // 正常,比较的结果为 true
//("blue", false) < ("purple", true) // 错误,因为 < 不能比较布尔类型
Swift 标准库只能比较七个以内元素的元组比较函数。如果你的元组元素超过七个时,你需要自己实现比较运算符
空合运算符:
空合运算符(a ?? b)将对可选类型 a 进行空判断,如果 a 包含一个值就进行解包,否则就返回一个默认值 b。表达式 a 必须是 Optional 类型。默认值 b 的类型必须要和 a 存储值的类型保持一致
a != nil ? a! : b
上述代码使用了三元运算符。当可选类型 a 的值不为空时,进行强制解封(a!),访问 a 中的值;反之返回默认值 b。无疑空合运算符(??)提供了一种更为优雅的方式去封装条件判断和解封两种行为,显得简洁以及更具可读性
let defaultColorName = "red"
var userDefinedColorName: String? //默认值为 nil
var colorNameToUse = userDefinedColorName ?? defaultColorName
// userDefinedColorName 的值为空,所以 colorNameToUse 的值为 "red"
userDefinedColorName 变量被定义为一个可选的 String 类型,默认值为 nil。由于 userDefinedColorName 是一个可选类型,我们可以使用空合运算符去判断其值。在上一个例子中,通过空合运算符为一个名为 colorNameToUse 的变量赋予一个字符串类型初始值。
由于 userDefinedColorName 值为空,因此表达式 userDefinedColorName ?? defaultColorName 返回 defaultColorName 的值,即 red。
如果你分配一个非空值(non-nil)给 userDefinedColorName,再次执行空合运算,运算结果为封包在 userDefaultColorName 中的值,而非默认值。
userDefinedColorName ="green"
colorNameToUse = userDefinedColorName ?? defaultColorName// userDefinedColorName 非空,因此 colorNameToUse 的值为 "green"
多行字符串字面量:
如果你需要一个字符串是跨越多行的,那就使用多行字符串字面量 — 由一对三个双引号包裹着的具有固定顺序的文本字符集
let quotation = """
The White Rabbit put on his spectacles. "Where shall I begin,
please your Majesty?" he asked.
"Begin at the beginning," the King said gravely, "and go on
till you come to the end; then stop."
"""
一个多行字符串字面量包含了所有的在开启和关闭引号(""")中的行。这个字符从开启引号(""")之后的第一行开始,到关闭引号(""")之前为止,
并且引号需要单独占两行
这就意味着字符串开启引号之后(""")或者结束引号(""")之前都没有换行符号
let singleLineString = "These are the same."
let multilineString = """
These are the same.
"""
一个多行字符串字面量能够缩进来匹配周围的代码。关闭引号(""")之前的空白字符串告诉 Swift 编译器其他各行多少空白字符串需要忽略。然而,如果你在某行的前面写的空白字符串超出了关闭引号(""")之前的空白字符串,则超出部分将被包含在多行字符串字面量中。
字符串是值类型:
在 Swift 中 String 类型是值类型。如果你创建了一个新的字符串,那么当其进行常量、变量赋值操作,或在函数/方法中传递时,会进行值拷贝。在前述任一情况下,都会对已有字符串值创建新副本,并对该新副本而非原始字符串进行传递或赋值操作
在实际编译时,Swift 编译器会优化字符串的使用,使实际的复制只发生在绝对必要的情况下,这意味着你将字符串作为值类型的同时可以获得极高的性能
字符串可以通过传递一个值类型为 Character 的数组作为自变量来初始化:
let catCharacters: [Character] = ["C","a","t","!",""]
let catString =String(catCharacters)
print(catString)// 打印输出:“Cat!”
连接字符串和字符
字符串可以通过加法运算符(+)相加在一起(或称“连接”)创建一个新的字符串
let string1 = "hello"
let string2 = " there"
var welcome = string1 + string2// welcome 现在等于 "hello there"
你也可以通过加法赋值运算符(+=)将一个字符串添加到一个已经存在字符串变量上:
var instruction ="look over"
instruction += string2// instruction 现在等于 "look over there"
你可以用 append() 方法将一个字符附加到一个字符串变量的尾部:
let exclamationMark:Character="!"
welcome.append(exclamationMark)// welcome 现在等于 "hello there!"
你不能将一个字符串或者字符添加到一个已经存在的字符变量上,因为字符变量只能包含一个字符。
let badStart = """
one
two
"""
let end = """
three
"""
print(badStart + end)// 打印两行:// one// twothree
let goodStart = """
one
two
"""
print(goodStart + end)// 打印三行:// one// two// three
字符串插值
字符串插值是一种构建新字符串的方式,可以在其中包含常量、变量、字面量和表达式。字符串字面量和多行字符串字面量都可以使用字符串插值。你插入的字符串字面量的每一项都在以反斜线为前缀的圆括号中
let multiplier = 3
let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"// message 是 "3 times 2.5 is 7.5"
插值字符串中写在括号中的表达式不能包含非转义反斜杠(\),并且不能包含回车或换行符。不过,插值字符串可以包含其他字面量。
比较字符串
字符串/字符相等
字符串/字符可以用等于操作符(==)和不等于操作符(!=)