1_基础部分
类型
基础数据类型:`Int`、`Double`、`Float`、`Bool`、`String`;
集合类型:`Array`、`Set`、`Dictionary`
元组:(Tuple)一组数据
? :(Optional)可选类型
常量和变量
let: 常量,不可变
var: 变量,可变
类型标注
var welcomeMessage: String
var red, green, blue: Double
一般来说你很少需要写类型标注。有初始值时,Swift可以推断出这个常量或者变量的类型
输出常量和变量
你可以用print(_:separator:terminator:)
函数来输出当前常量或变量的值;
\
: 字符串插值(string interpolation)
let friendlyWelcome = "Bonjour";
print(friendlyWelcome)
// 输出 "Bonjour!"
print("The friendlyWelcome is \(friendlyWelcome)")
// 输出 "The friendlyWelcome is Bonjour!
注释
// 这是一个注释
/* 这是一个,
多行注释 */
//可嵌套
/* 这是第一个多行注释的开头
/* 这是第二个被嵌套的多行注释 */
这是第一个多行注释的结尾 */
整数
Swift 提供了8,16,32和64位的有符号和无符号整数类型。
例:
UInt8:8位无符号整数类型是
Int32:32位有符号整数类型是
整数范围
访问不同整数类型的 min
和 max
属性来获取对应类型的最小值和最大值:
let minValue = UInt8.min // minValue 为 0,是 UInt8 类型
let maxValue = UInt8.max // maxValue 为 255,是 UInt8 类型
Int
Swift 提供了一个特殊的整数类型Int
,除非你需要特定长度的整数,否则使用 Int
就够了。这可以提高代码一致性和可复用性。即使是在32位平台上,Int
可以存储的整数范围也可以达到 -2,147,483,648
~ 2,147,483,647
,大多数时候这已经足够大了。
- 在32位平台上,
Int
和Int32
长度相同。 - 在64位平台上,
Int
和Int64
长度相同。
UInt
Swift 也提供了一个特殊的无符号类型 UInt
,长度与当前平台的原生字长相同:
- 在32位平台上,
UInt
和UInt32
长度相同。 - 在64位平台上,
UInt
和UInt64
长度相同。
注意:
尽量不要使用UInt
,除非你真的需要存储一个和当前平台原生字长相同的无符号整数。除了这种情况,最好使用Int
,即使你要存储的值已知是非负的。统一使用Int
可以提高代码的可复用性,避免不同类型数字之间的转换.
类型安全和类型推断
let meaningOfLife = 42
// meaningOfLife 会被推测为 Int 类型
如果你没有给浮点字面量标明类型,Swift 会推断你想要的是 Double
:
let pi = 3.14159
// pi 会被推测为 Double 类型
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
十进制浮点数也可以有一个可选的指数(exponent),通过大写或者小写的 e
来指定;
十六进制浮点数必须有一个指数,通过大写或者小写的 p
来指定。
如果一个十进制数的指数为 exp
,那这个数相当于基数和10^exp的乘积:
-
1.25e2
表示 1.25 × 10^2,等于125.0
。 -
1.25e-2
表示 1.25 × 10^-2,等于0.0125
。
如果一个十六进制数的指数为exp
,那这个数相当于基数和2^exp的乘积:
-
0xFp2
表示 15 × 2^2,等于60.0
。 -
0xFp-2
表示 15 × 2^-2,等于3.75
。
下面的这些浮点字面量都等于十进制的12.1875
:
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
整数转换
Int8
类型可以存储的数字范围是-128
`127`,而`UInt8`类型能存储的数字范围是`0`255
。如果数字超出了可存储的范围,编译的时候会报错:
let cannotBeNegative: UInt8 = -1
// UInt8 类型不能存储负数,所以会报错
let tooBig: Int8 = Int8.max + 1
// Int8 类型不能存储超过最大值的数,所以会报错
let twoThousand: UInt16 = 2_000
let one: UInt8 = 1
let twoThousandAndOne = twoThousand + UInt16(one)
SomeType(ofInitialValue)
是调用 Swift 构造器并传入一个初始值的默认方法。在语言内部,UInt16
有一个构造器,可以接受一个UInt8
类型的值,所以这个构造器可以用现有的 UInt8
来创建一个新的 UInt16
。注意,你并不能传入任意类型的值,只能传入 UInt16
内部有对应构造器的值。不过你可以扩展现有的类型来让它可以接收其他类型的值(包括自定义类型)
整数和浮点数转换
整数和浮点数的转换必须显式指定类型:
let three = 3
let pointOneFourOneFiveNine = 0.14159
let pi = Double(three) + pointOneFourOneFiveNine
// pi 等于 3.14159,所以被推测为 Double 类型
let integerPi = Int(pi)
// integerPi 等于 3,所以被推测为 Int 类型
注意:
结合数字类常量和变量不同于结合数字类字面量。字面量3
可以直接和字面量0.14159
相加,因为数字字面量本身没有明确的类型。它们的类型只在编译器需要求值的时候被推测。
类型别名
类型别名(type aliases)就是给现有类型定义另一个名字。你可以使用typealias
关键字来定义类型别名。
typealias AudioSample = UInt16
var maxAmplitudeFound = AudioSample.min
// maxAmplitudeFound 现在是 0
布尔值
Bool
: 布尔(Boolean)类型,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"
注意:
元组在临时组织值的时候很有用,但是并不适合创建复杂的数据结构。如果你的数据结构并不是临时使用,请使用类或者结构体而不是元组。
可选类型
可选类型(optionals)处理值可能缺失的情况。
例子:Swift 的 Int
类型有一种构造器,作用是将一个 String
值转换成一个 Int
值。然而,并不是所有的字符串都可以转换成一个整数。
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
注意:
Swift 的nil
和 Objective-C 中的nil
并不一样。在 Objective-C 中,nil
是一个指向不存在对象的指针。在 Swift 中,nil
不是指针——它是一个确定的值,用来表示值缺失。任何类型的可选状态都可以被设置为nil
,不只是对象类型。
if 语句以及强制解析
你可以使用 if
语句和 nil
比较来判断一个可选值是否包含值。
if convertedNumber != nil {
print("convertedNumber contains some integer value.")
}
// 输出 "convertedNumber contains some integer value."
强制解析(forced unwrapping):!
当确定可选类型确实包含值,在可选的名字后面加一个感叹号(!
)来获取值。
if convertedNumber != nil {
print("convertedNumber has an integer value of \(convertedNumber!).")
}
// 输出 "convertedNumber has an integer value of 123."
可选绑定
可选绑定(optional binding):判断可选类型是否包含值,如果包含就把值赋给一个临时常量或者变量。
if let constantName = someOptional {
statements
}
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"
可以包含多个可选绑定或多个布尔条件在一个 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
语句外且在语句后才能获取到值
隐式解析可选类型
声明的时候把感叹号放到类型的结尾.
一个隐式解析可选类型其实就是一个普通的可选类型,但是可以被当做非可选类型来使用.
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
的话,请使用普通可选类型。
错误处理
1.throws
当一个函数遇到错误条件,它能报错。调用函数的地方能抛出错误消息并合理处理。
func canThrowAnError() throws {
// 这个函数有可能抛出错误
}
一个函数可以通过在声明中添加throws
关键词来抛出错误消息。当你的函数能抛出错误消息时,你应该在表达式中前置try
关键词。
do {
try canThrowAnError()
// 没有错误消息抛出
} catch {
// 有一个错误消息抛出
}
一个do
语句创建了一个新的包含作用域,使得错误能被传播到一个或多个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]
作为参数。
2.断言
你可以调用 Swift 标准库的 assert(_:_:file:line:)
函数来写一个断言。向这个函数传入一个结果为 true
或者 false
的表达式以及一条信息,当表达式的结果为 false
的时候这条信息会被显示:
let age = -3
assert(age >= 0, "A person's age cannot be less than zero")
// 因为 age < 0,所以断言会触发
3.强制执行先决条件
当一个条件可能为false(假),但是继续执行代码要求条件必须为true(真)的时候,需要使用先决条件。例如使用先决条件来检查是否下标越界,或者来检查是否将一个正确的参数传给函数。
你可以使用全局 precondition(_:_:file:line:)
函数来写一个先决条件。向这个函数传入一个结果为 true
或者 false
的表达式以及一条信息,当表达式的结果为 false
的时候这条信息会被显示:
// 在一个下标的实现里...
precondition(index > 0, "Index must be greater than zero.")
你可以调用 precondition(_:_:file:line:)
方法来表明出现了一个错误,例如,switch 进入了 default 分支,但是所有的有效值应该被任意一个其他分支(非 default 分支)处理。
注意:
如果你使用unchecked模式(-Ounchecked)编译代码,先决条件将不会进行检查。编译器假设所有的先决条件总是为true(真),他将优化你的代码。然而,fatalError(_:file:line:)
函数总是中断执行,无论你怎么进行优化设定。
你能使用fatalError(_:file:line:)
函数在设计原型和早期开发阶段,这个阶段只有方法的声明,但是没有具体实现,你可以在方法体中写上fatalError("Unimplemented")作为具体实现。因为fatalError不会像断言和先决条件那样被优化掉,所以你可以确保当代码执行到一个没有被实现的方法时,程序会被中断。