简介
这篇文章主要用于记录自己学习Swift的过程, 也为自己之后回顾时做个参考.
文章是摘录自SwiftGG小组
翻译的官方文档, 仅对文档内容中与Objective-C
有差异的部分或比较重要的地方进行记录, 所以推荐具有Objective-C
一年以上实际开发经验的阅读.
基础部分
Swift 包含了 C 和 Objective-C 上的所有基础数据类型.
Int
表示整形.Double
和Float
表示浮点值.Bool
是布尔型值.String
的文本数据类型. 还有三个集合类型Array
, Set
, Dictionary
.
声明常量和变量
常量声明
: let number = 10
变量声明
: var name = "Lilei"
, var address: String
也可以在一行中声明多个常量或者变量, 用,
号隔开:
var x = 0.0, y = 1.0, z = 2.0
类型标注
var message: String
: 表示给message
变量添加了类型标注,message
变量可以存储String
类型的值.
你也可以在一行中定义多个同样的变量. 用逗号分隔, 并在最后一个变量名之后添加类型标注:
var red, green ,blue: Double
常量和变量的命名
在 Swift 中, 你可以使用任何你喜欢的字符作为常量和变量名, 包括Unicode
字符:
let π = 3.14159
let 哇偶 = "哈哈"
let 🐮🐶 = "奶牛小狗"
除此之外 Swift
还支持使用 Swift
保留的关键字做变量名:
let `class` = 5
但是非常不推荐这么使用.
注意:
以数字开头的命名仍然是不可取的.
输出常量和变量
Swift
中使用 print()
作为输出函数.
let name = "Lilei"
print(name) // 输出 Lilei
let age = 10
print("age == \(age)") // 输出 age == 10. 将其他类型转成String类只需要用 \( ) 包裹就可以.
print("The boy's name is " + name + "," + "he is " + "\(age)" + " years old");
//输出 The boy's name is Lilei,he is 10 years old. 可以使用 + 号拼接字符串.
类型安全和类型推断
由于 Swift 是类型安全的,所以它会在编译你的代码时进行类型检查(type checks), 并把不匹配的类型标记为错误。这可以让你在开发的时候尽早发现并修复错误.
当你在声明常量或者变量的时候赋给它们一个字面量(literal value 或 literal)
即可触发类型推断
let meaningOfLife = 42 // meaningOfLife 会被推测为 Int 类型
let anotherPi = 3 + 0.14159 // anotherPi 会被推测为 Double 类型
数值型字面量
一个十进制数,没有前缀
一个二进制数,前缀是0b
一个八进制数,前缀是0o
一个十六进制数,前缀是0x
let decimalInteger = 17
let binaryInteger = 0b10001 // 二进制的17
let octalInteger = 0o21 // 八进制的17
let hexadecimalInteger = 0x11 // 十六进制的17
数值类字面量可以包括额外的格式来增强可读性。整数和浮点数都可以添加额外的零并且包含下划线,并不会影响字面量:
let paddedDouble = 000123.456
let oneMillion = 1_000_000
let justOverOneMillion = 1_000_000.000_000_1
整数转换
let cannotBeNegative: UInt8 = -1
// UInt8 类型不能存储负数,所以会报错
let tooBig: Int8 = Int8.max + 1
// Int8 类型不能存储超过最大值的数,所以会报错
要将一种数字类型转换成另一种,你要用当前值来初始化一个期望类型的新数字,这个数字的类型就是你的目标类型。
let ten: Int = 10;
let half: Double = 0.5;
let tenHalf = Double(ten) + half; // 将Int型的ten转换成Double类型 10.5;
SomeType(ofInitialValue)
是调用 Swift 构造器并传入一个初始值的默认方法. 你并不能传入任意类型的值,只能传入 SomeType 内部有对应构造器的值. 所以, 之所以我们可以使用 Double(IntValue)
将 Int
类型的常量转换成 Double
, 是因为 Double
内部有该构造器. 你还可以扩展现有的类型来让它可以接收其他类型的值(包括自定义类型).
类型别名
类型别名(type aliases)
就是给现有类型定义另一个名字。你可以使用typealias
关键字来定义类型别名.
typealias AudioSample = UInt16
Bool值
Swift 有一个基本的布尔(Boolean)类型,叫做Bool
。布尔值指逻辑上的值,因为它们只能是真或者假。Swift 有两个布尔常量,true
和 false
.
如果你在需要使用 Bool
类型的地方使用了非布尔值,Swift 的类型安全机制会报错。下面的例子会报告一个编译时错误:
let i = 1
if i {
// 这个例子不会通过编译,会报错
}
下面这个是合法的:
let i = 1
if i == 1 {
// 这个例子会编译成功
}
元祖
元组(tuples)把多个值组合成一个复合值。元组内的值可以是任意类型,并不要求是相同类型.
let http404Error = (404, "Not Found")
// http404Error 的类型是 (Int, String),值是 (404, "Not Found")
你可以将一个元组的内容分解(decompose)成单独的常量和变量,然后你就可以正常使用它们了:
let (statusCode, statusMessage) = http404Error
print("The status code is \(statusCode)")
// 输出 "The status code is 404"
print("The status message is \(statusMessage)")
// 输出 "The status message is Not Found"
如果你只需要一部分元组值,分解的时候可以把要忽略的部分用下划线_
标记:
let (justTheStatusCode, _) = http404Error
print("The status code is \(justTheStatusCode)")
// 输出 "The status code is 404"
此外,你还可以通过下标来访问元组中的单个元素,下标从零开始:
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"
注意:
元组在临时组织值的时候很有用,但是并不适合创建复杂的数据结构。如果你的数据结构并不是临时使用,请使用类或者结构体而不是元组。
可选值
C
和 Objective-C
中并没有可选类型这个概念。最接近的是 Objective-C
中的一个特性,一个方法要不返回一个对象要不返回nil
,nil
表示"缺少一个合法的对象"。然而,这只对对象起作用——对于结构体,基本的 C
类型或者枚举类型不起作用。对于这些类型,Objective-C
方法一般会返回一个特殊值(比如NSNotFound
)来暗示值缺失。这种方法假设方法的调用者知道并记得对特殊值进行判断.然而,Swift
的可选类型可以让你暗示任意类型的值缺失,并不需要一个特殊值.
一个可选的 Int
被写作 Int?
而不是 Int
.问号暗示包含的值是可选类型,也就是说可能包含 Int
值也可能不包含值.
nil
nil
不能用于非可选的常量和变量。如果你的代码中有常量或者变量需要处理值缺失的情况,请把它们声明成对应的可选类型。
//如果你声明一个可选常量或者变量但是没有赋值,它们会自动被设置为 nil:
var surveyAnswer: String?
// surveyAnswer 被自动设置为 nil
注意:
Swift
的nil
和Objective-C
中的nil
并不一样。在Objective-C
中,nil
是一个指向不存在对象的指针。在Swift
中,nil
不是指针——它是一个确定的值,用来表示值缺失。任何类型的可选状态都可以被设置为nil
,不只是对象类型.
if 语句以及强制解析
if
判断不等:
if some != nil {
print("some is not nil")
}
当你确定可选类型确实包含值之后,你可以在可选的名字后面加一个感叹号!
来获取值。这个惊叹号表示"我知道这个可选有值,请使用它。"这被称为可选值的强制解析(forced unwrapping
).
if some != nil {
print("some is not nil, is \(some!)")
}
注意: 使用
!
来获取一个不存在的可选值会导致运行时错误。使用!
来强制解析值之前,一定要确定可选包含一个非nil
的值.
可选绑定
可选绑定可以用在 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")
}
这段代码可以被理解为:
“如果 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")
}
// 输出 "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"
注意: 在
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 // 不需要感叹号
你可以把隐式解析可选类型当做一个可以自动解析的可选类型。你要做的只是声明的时候把感叹号放到类型的结尾,而不是每次取值的可选名字的结尾。
注意:
如果你在隐式解析可选类型没有值的时候尝试取值,会触发运行时错误。和你在没有值的普通可选类型后面加一个惊叹号一样。
如果一个变量之后可能变成nil
的话请不要使用隐式解析可选类型。如果你需要在变量的生命周期中判断是否是nil
的话,请使用普通可选类型。
### 错误处理
使用throws
来标注函数会抛出异常.
func canThrowAnError() throws {
// 这个函数有可能抛出错误
}
当你的函数能抛出错误消息时, 你应该在表达式中前置try
关键词。
do {
try canThrowAnError()
// 没有错误消息抛出
} catch {
// 有一个错误消息抛出
}
这里有一个错误处理如何用来应对不同错误条件的例子。
func makeASandwich() 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]
作为参数。
基本预算符
除了Objective-C
原有的基本运算符之外,Swift
又新增了如下几个运算符
空合运算符(Nil Coalescing Operator)
空合运算符(a ?? b)
将对可选类型 a
进行空判断,如果 a
包含一个值就进行解封,否则就返回一个默认值 b
。表达式 a
必须是 Optional
类型。默认值 b
的类型必须要和 a
存储值的类型保持一致。
a ?? b
就相当于 a != nil ? a! : b
区间运算符(Range Operators)
Swift
提供了两个方便表达一个区间的值的区间运算符。
闭区间运算符
闭区间运算符(a...b
)定义一个包含从 a
到 b
(包括 a
和 b
)的所有值的区间。a
的值不能超过 b
。
for index in 1...5 {
print("\(index) * 5 = \(index * 5)")
}
// 1 * 5 = 5
// 2 * 5 = 10
// 3 * 5 = 15
// 4 * 5 = 20
// 5 * 5 = 25
半开区间运算符
半开区间运算符(a..<b
)定义一个从 a
到 b
但不包括 b
的区间。 之所以称为半开区间,是因为该区间包含第一个值而不包括最后的值。
let names = ["Anna", "Alex", "Brian", "Jack"]
let count = names.count
for i in 0..<count {
print("第 \(i + 1) 个人叫 \(names[i])")
}
// 第 1 个人叫 Anna
// 第 2 个人叫 Alex
// 第 3 个人叫 Brian
// 第 4 个人叫 Jack