基础部分
swift 包含了 C 以及 Objective-C 上所有的基础数据类型。
Int |
整型值 |
---|---|
Double 和 Float |
浮点型 |
Bool |
布尔型值 |
String |
文本类型数据 |
Array |
数组 |
Set |
集合 |
Dictionary |
字典 |
swift增加了Objective-C中没有的高阶数据类型:元组(Tuple)。元组可以让你创建或者传递一组数据。
Swift 增加了 可选(Optional)类型,用于处理值缺失的情况。
Swift 是一门类型安全的语言。
变量和常量
常量和变量必须在使用前声明,let
:常量 var
:变量
let maxNumber = 10
var currentTemp = 0
你可以再一行中声明多个常量或多个变量,用逗号隔开
let x = 0.0, y = 9.9, z = 9.9
注意
如果你的代码中有不需要改变的值,请使用
let
关键字将他声明为常量。
类型注解
声明常量或者变量的时候,可以加上类型注释(type annotation), 说明常量或者变量中要存储的值的类型。
var welcomeMessage: String
你可以在一行中定义多个同样类型的变量,用逗号分割,并在最后一个变量名之后添加类型注解:
var red, green, blue: Double
注意
一般来说你很少需要写类型注解。
如果你在声明常量或者变量的时候赋了初始值,Swift可以推断出这个常量或者变量的类型。
常量和变量的命名
常量和变量名激活包括所有的字符,包括Unicode字符:
let π = 3.14159
let 你好 = "你好世界"
let = "dogcow"
常量和变量名不能包含数学符号,箭头,不能以数字开头。
输出常量和变量
可以用print(_:separator:terminator:)
函数来输出当前常量或变量的值:
let a = "Candy"
print(candy)
// 输出 “Candy”
插值
let day = "Sunday"
print("The day is \(day)")
注释
// 这是当行注释
/**
* 这是多行注释
*/
分号
Swift 并不强制要求你在每条语句的结尾使用分号(;
)
let cat = ""; print(cat)
整数
整数范围
Swift 提供了 8、16、32和64位的 有符号 和 无符号证书类型。命名方式和C语音很像。
let minValue = UInt8.min // minValue为0
let maxValue = UInt8.max // maxValue为255
Int
一般情况下,不需要指定整数的长度。默认情况下
在32位平台上:
Int
和Int32
长度相等在64位平台上:
Int
和Int64
长度相等
即使是在32位平台上,Int
可以存储的整数范围也可以达到 -2,147,483,648
~ 2,147,483,647
。
UInt
Swift 也提供了一个特殊的无符号类型 UInt
,长度与当前平台的原生字长相同:
- 在32位平台上,
UInt
和UInt32
长度相同。 - 在64位平台上,
UInt
和UInt64
长度相同。
注意
尽量不要使用
UInt
,除非你真的需要存储一个和当前平台原生字长相同的无符号整数。除了这种情况,最好使用Int 。
即使需要存储的值是 非负的。
统一使用
Int
可以提高代码的复用性,避免不同类型数字之间的转换。
浮点数
浮点类型比整数类型表示的范围更大,可以存储比 Int
类型更大或者更小的数字。Swift 提供了两种有符号浮点数类型:
-
Double
表示64位浮点数。当你需要存储很大或者很高精度的浮点数时请使用此类型。 -
Float
表示32位浮点数。精度要求不高的话可以使用此类型。
注意
Double
精确度很高,至少有 15 位小数,而Float
只有 6 位小数。选择哪个类型取决于你的代码需要处理的值的范围,在两种类型都匹配的情况下,将优先选择Double
。
类型安全和类型判断
Swift 是一个类型安全(type safe)的语言. 它会在编译代码的时候进行类型检查 (type checks), 把不匹配的类型标记为错误,可以让你在开发过程中发现并修复错误。
当你要处理不同类型的值时,类型检查可以帮你避免错误。然而,这并不是说 每次声明 常量、变量的时候都需要指定类型。如果你没有显示指定类型,Swift会使用类型判断(type inference)来选择合适的类型。
例如:
let a = 42
// a 会被推测为 Int
let b = 3.14
// b 会被推测为 Double
let c = 3 + 0.14
// c 会被推测为 Double
数值型字面量
整数字面量可以写作:
- 十进制:没有前缀
- 二进制:前缀 0b
- 八进制: 前缀 0o
- 十六进制:前缀 0x
例如:
let a = 17
let b = 0b1001
let c = 0o21
let d = 0x11
数值类字面量可以通过额外的格式来增强可读性。 整数和浮点数都可以添加额外的零并且包含下划线,并不会影响字面量:
let a = 000123.456
let b = 1_000_000
let c = 1_000_000.000_000_1
数值型类型转换
通常来讲,即使代码中的整数常量和变量已知非负,也请使用 Int
类型。
总是使用默认的整数类型可以保证 整数常量和变量 可以直接使用并复用
并且可以匹配 整数类字面量 的 类型推断。
整数转换
不通整数类型的变量和常量可以存储不通的数字。
Int8
类型的常量或者变量可以存储的数字范围:-128 ~ 127
而UInt
类型的常量或者变量能存储的数字范围:0 ~ 255
let cannotBeNagetive: UInt8 = -1
// UInt8 类型不能存储负数,所以会报错
let tooBig : Int8 = Int8.Max + 1
// Int8 类型不能存储超过最大数,所以会报错
由于每种整数类型都可以存储不通范围的值,所以你必须根据不同情况,选择性使用 数值型 类型转换。
这种选择性使用的方式,可以预防隐士转换的错误,并且让你的代码中的类型转换意图变得清晰。
/**
* 因为类型不同,不能直接相加
* 调用Int16(one) 来创建一个新的 UInt16 数字,才能相加
*/
let twoThousand: UInt16 = 2_000
let one: UInt8 = 1
let twoThousandAndOne = twoThousand + UInt(one)
整数和浮点数转换
let three = 3
let pointOneFourOneFiveNice = 0.14159
let pi = Double(three) + pointOneFourOneFiveNice
// pi 等于3.14159 所以被推测为 Double 类型
let integerPi = Int(pi)
// integerPi 等于 3 , 所以被推测为 Int 类型
注意:
当用这种方式来初始化一个新的浮点数值,浮点会被阶段。
也就是说 4.71 会变成 4 , -3.9 会变成-3
类型别名
类型别名 (type aliases) 就是给现有类型定义另一个名字。例如:
typealias AudioSample = UInt16
var maxAmplitudeFound = AudioSample.min
布尔值
Swift 有一个基本的布尔(Boolean
)类型, 加做 Bool
。
Swift 有两个布尔常量,true
和 false
let orangesAreOrange = true
let turnipsAreDelicious = false
// 条件语句
if turnipsAreDelicious {
print("Mmm, tasyty turnips!")
} else {
print("Eww, turnips are horrible")
}
元组
元祖(tuples) 把多个值组合成一个复合值。元组内可以是任意类型,并不要求是相同类型。
例如:
let httpError = (404, "Not Found")
let (statusCode, statusMessage) = httpError
print("The status code is \(statusCode)")
//输出“The status code is 404”
print("The status message is \(statusMessage)")
// 输出“The status message is Not Found”
当你只需要一部分的元组值的时候,可以把忽略的部分用下划线(_
)标记:
let (statusCode, _) = httpError
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”
可选类型
使用可选类型(optionals) 来处理可能缺失的情况。可选表示两种可能:
- 可能有值:通过解析来访问可选值
- 没有值
注意:
C
和Objective-C
中并没有可选类型
这个概念。最接近的是Objective-C中的一个特性,方法返回值:对象 or
nil
,nil
表示"缺少一个合法的对象"。
Swift
的可选类型可以让你暗示任意类型的缺失。
例如:
let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
// convertedNumber 被推测为类型“Int?” , 或者类型为"Optional Int"
因为该构造器可能会失败,所以返回一个可选类型(optional)Int, 而不是一个Int
.
一个可选的Int
被写成Int?
。问号表示暗示包含的值是可选类型的。也就是说可能包含Int
也可能不包含值。
nil
你可以给可选变量赋值为nil
,表示它没有值:
var a: Int? = 404
// a 包含一个可选的 Int 值 404
a = nil
// a 现在不包含值
注意:
nil
不能用于非可选的常量和变量。如果代码中有常量或者变量需要处理值缺失的情况,请把它们声明成对应的可选类型。
如果声明一个可选常量或者变量,但是没有赋值,它们会自动被设置成nil
:
var temp: String?
// temp 被自动设置为nil
注意:
Swift 的
nil
和 Objective-C 中的nil
并不一样。OC:
nil
是一个指向不存在对象的指针。Swift:
nil
不是指针。它是一个确定的值,用来表示值缺失。任何类型的可选状态都可以被设置为nil,不只是对象类型。
if语句以及强制解析
你可以使用if
语句 和 nil
比较来判断一个可选值是否包含值。
如果有可选类型有值,它将不等于nil
if temp != nil {
print('temp contain some value')
}
当你确定可选类型确实包含值之后,你可以在可选的名字后面加感叹号(!
)来获取值。
这个感叹号表示“我知道这个可选有值,请使用它”。这个称为可选值的强制解析(forced unwarpping):
var temp: String?
if temp != nil {
print("temp value is \(temp!)")
}
// 输出 temp value is 123
注意:
使用
!
来获取一个不存在的可选值,会导致运行错误。 使用!
来强制解析之前,一定要确定可选包含一个非nil
的值。
可选绑定
使用可选绑定(optional binding) 来判断可选类型是否包含值, 如果包含 就把值 赋给一个临时常量 或者 变量。
可选绑定 可以用在 if
和 while
语句中, 这条语句不仅可以用来判断可选类型中是否有值,同时可以将可选类型中的值 赋给 一个常量或者变量。
例如:
if let constantName = someOptional {
statements
}
如果Int(number)
返回的可选Int
包含一个值,创建一个 temp的新常量并且将可选包含的值给它
if let temp = Int(number) {
print("\(number) has an integer value of \(temp)")
} else {
print("\(number) could not be converd to an integer")
}
你可以包含多个可选绑定 或者 布尔条件在一个if
语句中,只需要用逗号分开就行。
只要有任意一个可选绑定的值为nil
, 或者任意 bool
条件为 false
,则整个if
条件判断为false
。
例如:
if let firstNumber = Int("4"), let secondNumber = Int("42"), firstNmuber < secondNumber {
print("\(firstNumber) < \(secondNumber)")
}
注意
在
if
条件语句中使用常量和变量来创建一个可选绑定,仅在if
语句的句中(body)
才能获取到值。相反,在
guard
语句中使用常量和变量来创建一个可选绑定,仅在guard
语句外且语句后才能获取到值。
隐式解析可选类型
可选类型暗示了 变量或常量 可以没有值。 可选 可以通过 if语句来判断是否有值,如果有值的话,可以通过可选绑定来解析值。
有时候,第一次被赋值之后,可以确定一个可选类型总是有值。在这种情况下,每次判断和解析可选值 是 非常低效的,因为可以确定它总会有值。
这种类型的可选状态可以呗定义为:隐式解析可选类型(implicitly unwrapped optionals)。
把想要用作可选类型后面的问号(String?
)改成感叹号(String!
) 来声明一个隐式解析可选类型,可以再定义它时,直接把感叹号放在可选类型的后面。
let possiableString: String? = "An optional String."
let forceString: String = possableString! // 需要感叹号来获取值
let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // 不需要感叹号
你可以把隐式解析可选类型当做一个可以自动解析的可选类型。
当你使用一个隐式解析可选值时,Swift 首先把她当做普通的可选值;
如果它不能被当成可选类型使用,swift会强制解析可选值。
在以上的代码中,可选值`
let possiableString: String? = "An optional String."
let forceString: String = possableString! // 需要感叹号来获取值
let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // 不需要感叹号
你可以把隐式解析可选类型当做一个可以自动解析的可选类型。
当你使用一个隐式解析可选值时,Swift 首先把她当做普通的可选值;
如果它不能被当成可选类型使用,swift会强制解析可选值。
在以上的代码中,可选值`
let possiableString: String? = "An optional String."
let forceString: String = possableString! // 需要感叹号来获取值
let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // 不需要感叹号
你可以把隐式解析可选类型当做一个可以自动解析的可选类型。
当你使用一个隐式解析可选值时,Swift 首先把她当做普通的可选值;
如果它不能被当成可选类型使用,swift会强制解析可选值。
在以上的代码中,可选值`
let possiableString: String? = "An optional String."
let forceString: String = possableString! // 需要感叹号来获取值
let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // 不需要感叹号
你可以把隐式解析可选类型当做一个可以自动解析的可选类型。
当你使用一个隐式解析可选值时,Swift 首先把她当做普通的可选值;
如果它不能被当成可选类型使用,swift会强制解析可选值。
在以上的代码中,可选值assumedString
在把自己的值给implicitString
之前会被强制解析,原因是
implicitString
本身的类型是非可选类型的String
。
下面的代码中,optionalString 并没有显示的数据类型。那么根据类型判断,它是一个普通的可选类型。
let optionString = assumedString
// optionalString 的类型是 "String?",assumedString 也没有被强制解析。
错误处理
可以使用 错误处理(error handling) 来处理执行中可能遇到的错误条件
当一个函数遇到错误条件,它能报错。调用函数的地方能抛出错误消息并合并处理
func canThrowAnError() throws{
}
一个函数可以通过在声明中添加 throws
关键词来抛出错误消息。
当抛出错误消息时,你应该在表达式中前置 try
关键词
do{
try canThrowAnError()
// 没有错误消息抛出
} catch {
// 哟一个错误消息抛出
}
一个do
语句创建了一个新的包含作用域, 使得错误能被传播到一个或者多个catch
从句
例如:
func makeASandWich() throws {
// ...
}
do {
try makeASandWich()
eatASandWich()
} catch SandwichError.outOfCleanDishes{
} catch SandwichError.missingIngredients(let ingredients){
buyGroceries(ingredients)
}
断言和先决条件
断言和先决条件是在运行时所做的检查。你可以使用他们来检查执行后续代码之前,是否一个必要的条件已经满足了。如果断言或者先决条件中的布尔条件评估结果为true,则代码会继续执行,如果是false,当前状态无效,代码执行结束。
断言帮助你在开发阶段找到错误和不正确的假设,先决条件帮助你在生产环境中探测到存在的问题。
断言和先决条件的不同点是,他们什么时候进行状态检测:断言仅在调试环境运行,而先决条件则在调试环境和生产环境中运行。在生产环境中,断言的条件将不会进行评估。这个意味着你可以使用很多断言在你的开发阶段,但是这些断言在生产环境中不会产生任何影响。
使用断言进行调试
let age = -3
assert(age >= 0, "A person's age cannot be less than zero")
// 因为age = -3 , 所以断言能触发
强制执行先决条件
当一个条件可能为假,但是继续执行代码要求必须为真的时候,可以使用先决条件。
你可以使用全局preconditon(_:_:file:line:)
函数来写一个先决条件。向这个函数传入一个结果为true
或者false
的表达式 以及 一条信息,当表达式的结果为 false
的时候这条信息会被显示:
// 例如使用先决条件来检查是否下标越界,或者来检查是否将一个正确的参数传给函数。
precondition(index > 0, "Index Must be greater than zero.")
// 在一个下标里的实现
注意
如果你使用 unchecked 模式(-Ounchecked)编译代码,先决条件将不会进行检查。编译器假设所有的先决条件总是为 true(真),他将优化你的代码。然而,
fatalError(_:file:line:)
函数总是中断执行,无论你怎么进行优化设定。你能使用
fatalError(_:file:line:)
函数在设计原型和早期开发阶段,这个阶段只有方法的声明,但是没有具体实现,你可以在方法体中写上 fatalError("Unimplemented")作为具体实现。因为 fatalError 不会像断言和先决条件那样被优化掉,所以你可以确保当代码执行到一个没有被实现的方法时,程序会被中断。