Swift 编程基础:可选类型与控制语句详解
1. 可选类型(Optionals)
在 Swift 中,可选类型是一个重要的概念,它用于处理可能没有值的情况。以下我们通过几个示例来深入理解可选类型。
1.1 可选类型的基本概念
先看一个字典的例子:
var dict: [String: Int]
dict = ["Red": 0, "Green": 1, "Blue": 2]
let value = dict["Red"]
尽管字典中的值都是
Int
类型,但
value
的推断类型是
Int?
,即可选整数。
?
表示该类型是可选的。那么可选意味着什么,为什么这里要使用它呢?当我们使用一个不存在的键来访问字典时,就会体现可选类型的作用。
let yellowValue = dict["Yellow"]
对于
yellowValue
应该赋予什么值呢?在大多数语言中,会有一个特殊值来表示“没有值”。例如,在 Objective - C 中是
nil
,在 C 和 C++ 中是
NULL
,在 Java 中是
null
。但使用这些值可能会很危险,比如在 Java 中使用
null
引用会引发异常,在 C 和 C++ 中可能导致应用程序崩溃,而且很难判断一个变量是否可能包含
null
值。
Swift 通过一种简洁的方式解决了这个问题:它有
nil
值,但只有当变量或常量被声明为可选类型,或者其类型被推断为可选类型时,才能将其设置为
nil
。这样,我们通过检查变量或常量的类型,就能立即知道它是否可能为
nil
。
以下是一个变量
color
的例子:
var color = "Red"
由于其定义方式,
color
的推断类型是
String
,不是可选类型,因此将
nil
赋值给它是非法的:
color = nil // Illegal: color is not an optional type.
这意味着我们无需担心
color
会有
nil
值。同时,即使我们知道字典返回的值不为
nil
,也不能用
color
来存储字典返回的值:
let dict = [0: "Red", 1: "Green", 2: "Blue"]
color = dict[0] // Illegal: value of dict[0] is optional string, color is not optional.
要使这个赋值合法,我们需要将
color
的类型从
String
改为可选
String
:
let dict = [0: "Red", 1: "Green", 2: "Blue"]
var color: String? // "String?" means optional String
color = dict[0] // Legal
println(color) // What does this print?
1.2 可选类型的解包
将上述代码输入到 playground 中,会发现打印的不是
"Red"
,而是
Optional("Red")
。字典访问返回的是被包装为可选类型的值,要获取字符串值,需要使用
!
运算符来“解包”可选类型:
let actualColor = color! // "color!" means unwrap the optional
actualColor
的推断类型是
String
,其值为
"Red"
。这表明,从字典中检索值时,得到的是可选类型,必须解包才能获得所需的值。
然而,解包可选类型可能很危险。例如:
let dict = [0: "Red", 1: "Green", 2: "Blue"]
let color = dict[4]
let actualColor = color!
Swift 正确推断出
color
的类型是
String?
,但当解包字典访问的结果并将值赋给
actualColor
时,会出现错误。在 playground 中会报告错误,在应用程序中则会导致崩溃。这是因为字典中没有键为
4
的条目,所以
color
被赋值为
nil
,解包
nil
会导致崩溃。
不过,Swift 提供了三种处理可选值为
nil
的方法:
-
方法一:检查是否为
nil
再解包
if color != nil {
let actualColor = color!
}
-
方法二:使用
if let语法
if let actualColor = color {
// Executed only if color was not nil
println(actualColor)
}
如果
color
不为
nil
,则执行代码块,并将解包后的值赋给常量
actualColor
。也可以将
let
改为
var
,使
actualColor
成为变量。
if var actualColor = color {
// Executed only if color was not nil. actualColor can be modified
println(actualColor)
}
-
方法三:使用合并运算符
??
let dict = [0: "Red", 1: "Green", 2: "Blue"]
let color = dict[4]
let actualColor = color ?? "Blue"
??
运算符会解包其左操作数(如果它不是
nil
)并返回其值,否则返回第二个操作数。在这个例子中,由于
color
为
nil
,
actualColor
会被赋值为
"Blue"
。可以将代码简化为:
let dict = [0: "Red", 1: "Green", 2: "Blue"]
let actualColor = dict[4] ?? "Blue"
1.3 可选类型的其他应用场景
可选类型不仅在字典中使用,在 Swift API 文档中,有很多方法会返回可选值,甚至类型初始化器也可能返回可选值。例如:
enum ResultType : String {
case SUCCESS = "Success"
case WARNING = "Warning"
case ERROR = "Error"
}
let result = ResultType(rawValue: "Invalid")
result
的推断类型是
ResultType?
,由于没有原始值为
"Invalid"
的枚举情况,其值为
nil
。
有时候,解包可选类型既不方便也不必要。例如,在类中定义一个变量,在类初始化时无法初始化,但在代码使用前会有有效值。这时,我们可以使用隐式解包可选类型。
let dict = [0: "Red", 1: "Green", 2: "Blue"]
var color: String! // Notice the !
color = dict[0] // Assigns Optional("Red")
println(color) // Automatically unwraps the optional
变量
color
被声明为
String!
类型,称为隐式解包可选类型。
!
告诉 Swift 假设该变量在使用时总是有非
nil
值,并自动解包。但要注意,如果
color
实际上为
nil
,使用时会导致应用程序崩溃。
1.4 处理
NSDictionary
的值
我们可以使用可选类型来处理从
NSDictionary
获取的值。首先创建一个
Foundation
字典:
let d = NSDictionary(objects: ["Red", "Green", "Blue"],
forKeys: [0, 1, 2])
let dict = d as [Int: String]
当使用
d[1]
访问字典时,Swift 推断其类型为
AnyObject?
,可以通过显式转换来消除警告:
let color = d[1] as String?
如果字典的类型错误,例如有人在文件中放入了错误的数据,实际得到的是
[String: String]
类型的字典,使用
as
进行类型转换会导致崩溃。可以使用
as?
运算符来避免这种情况:
let d = NSDictionary(objects: ["Red", "Green", "Blue"],
forKeys: ["0", "1", "2"])
if let dict = d as? [Int: String] { // as? returns nil if d is not of type [Int: String]
println("OK") // As expected – use dict as normal
} else {
println("Incorrect types") // Executed if d is not of type [Int: String]
}
也可以使用
is
关键字在转换前检查字典是否为预期类型:
if d is [Int: String] { // Evaluate to true if d maps Ints to Strings
println("Is [Int: String]")
} else {
println("Incorrect types")
}
2. 控制语句
Swift 支持大多数其他语言中的控制语句,并为其中一些添加了有趣的新特性。
2.1
if
语句
Swift 的
if
语句与其他语言类似,用于测试布尔条件,只有当条件为
true
时才执行代码。可以选择性地包含
else
块,当条件为
false
时执行。
let random = arc4random_uniform(10)
if random < 5 {
println("Hi")
} else {
println("Ho")
}
arc4random_uniform()
函数返回一个介于 0(包含)和其参数值(不包含)之间的随机整数。Swift 不要求将测试条件放在括号中,但执行的代码必须用花括号括起来,即使只有一行代码。
if random < 5 { // Parentheses not required
}
if (random < 5) { // But can be used
}
以下代码在 Swift 中是无效的:
if random < 5
println("Hi") // Invalid: must be in braces
else
println("Ho") // Invalid: must be in braces
Swift 还支持三元运算符
?:
,可以将上述代码改写为:
let random = arc4random_uniform(10)
random < 5 ? println("Hi") : println("Ho")
进一步简化:
let random = arc4random_uniform(10)
println(random < 5 ? "Hi" : "Ho")
2.2
for
语句
Swift 有两种
for
语句变体。
-
第一种变体
:类似于基于 C 语言的
for
循环。
for var i = 0; i < 10; i++ {
println(i)
}
与
if
语句一样,不需要将第一部分括在括号中,但循环代码块的花括号是必需的。可以使用
break
语句提前结束循环,或使用
continue
语句中断当前迭代并开始下一次迭代。
-
第二种变体
:用于迭代值序列,如范围或集合。
for i in 0..<10 {
println(i)
}
使用这种形式时,不需要使用
var
来声明变量。还可以使用
stride()
函数创建并迭代更通用的范围。
for i in stride(from: 10, through: 0, by: -2) {
println(i)
}
迭代数组元素非常简单:
let strings = ["A", "B", "C"]
for string in strings {
println(string)
}
迭代字典的键:
let d = [ 0: "Red", 1: "Green", 2: "Blue"]
for key in d.keys {
println("\(key) -> \(d[key])")
}
也可以直接迭代字典的键值对:
for (key, value) in d {
println("\(key) -> \(value)")
}
2.3
do
和
while
语句
do
和
while
语句与 C、C++、Java 和 Objective - C 中的对应语句相同。它们都会执行一段代码,直到满足某个条件。不同之处在于,
while
语句在每次循环开始前测试条件,而
do
语句在每次循环结束时测试条件。
var i = 10
while i > 0 {
println(i--)
}
var j = 10
do {
println(j--)
} while j > 0
2.4
switch
语句
Swift 的
switch
语句非常强大。以下是一个根据变量
value
的值打印不同内容的例子:
var value: UInt = 11
switch value {
case 2, 3, 5, 7, 11, 13, 17, 19:
println("Count is prime and less than 20")
case 20...30:
println("Count is between 20 and 30")
default:
println("Greater than 30")
}
关于这个代码,有几点需要注意:
- 一个
case
可以有多个可能的值,用逗号分隔。
-
case
值可以是一个范围,
switch
语句能够进行模式匹配。
- 控制流不会像大多数其他语言那样从一个
case
传递到另一个
case
,因此不需要在
case
之间使用
break
语句。如果需要继续执行下一个
case
,可以在第一个
case
末尾添加
fallthrough
语句。
-
case
列表必须详尽,如果没有包含
default
情况,编译器会报错。每个
case
必须至少有一行可执行代码。
以下是一个根据字符串值进行
switch
的例子:
let s = "Hello"
switch s {
case "Hello":
println("Hello to you, too")
case "Goodbye":
println("See you tomorrow")
default:
println("I don't understand")
}
总结
通过以上内容,我们详细了解了 Swift 中的可选类型和控制语句。可选类型帮助我们安全地处理可能没有值的情况,而各种控制语句为我们提供了丰富的编程逻辑实现方式。在实际编程中,合理运用这些特性可以提高代码的安全性和可读性。
以下是一个简单的流程图,展示了从字典中获取值并处理可选类型的过程:
graph TD;
A[访问字典] --> B{值是否为 nil};
B -- 是 --> C[使用默认值或进行错误处理];
B -- 否 --> D[解包值];
D --> E[使用解包后的值];
同时,为了更清晰地对比不同语言中表示“没有值”的特殊值,我们列出以下表格:
| 语言 | 表示“没有值”的特殊值 |
| ---- | ---- |
| Objective - C | nil |
| C 和 C++ | NULL |
| Java | null |
| Swift | nil(仅用于可选类型) |
Swift 编程基础:可选类型与控制语句详解
3. 可选类型和控制语句的综合应用示例
为了更好地理解可选类型和控制语句在实际编程中的应用,我们来看一个综合示例。假设我们要编写一个程序,用于处理用户输入的命令,并根据命令执行相应的操作。用户输入的命令可能是有效的,也可能是无效的,这就需要使用可选类型来处理可能的无效输入。
// 定义命令枚举
enum Command {
case Start
case Stop
case Pause
case Unknown
}
// 根据输入字符串返回对应的命令
func getCommand(from input: String?) -> Command {
guard let input = input else {
return .Unknown
}
switch input.lowercased() {
case "start":
return .Start
case "stop":
return .Stop
case "pause":
return .Pause
default:
return .Unknown
}
}
// 模拟用户输入
let userInput: String? = "Start"
let command = getCommand(from: userInput)
// 根据命令执行相应操作
switch command {
case .Start:
println("Starting the process...")
case .Stop:
println("Stopping the process...")
case .Pause:
println("Pausing the process...")
case .Unknown:
println("Unknown command. Please try again.")
}
在这个示例中,我们首先定义了一个
Command
枚举,用于表示不同的命令。
getCommand(from:)
函数接受一个可选的字符串输入,并返回对应的
Command
枚举值。如果输入为
nil
,则返回
.Unknown
。在获取用户输入并得到命令后,我们使用
switch
语句根据命令执行相应的操作。
4. 常见错误及避免方法
在使用可选类型和控制语句时,可能会遇到一些常见的错误。以下是一些常见错误及避免方法的总结:
| 常见错误 | 错误原因 | 避免方法 |
|---|---|---|
解包
nil
导致崩溃
|
尝试对值为
nil
的可选类型进行解包
|
在解包前检查可选类型是否为
nil
,使用
if let
或
??
运算符
|
| 忘记使用花括号 |
在
if
、
for
等语句中,忘记将代码块用花括号括起来
| 养成使用花括号的习惯,即使代码块只有一行 |
switch
语句
case
列表不详尽
|
没有包含所有可能的情况,且没有
default
情况
|
确保
switch
语句的
case
列表涵盖所有可能的值,或者添加
default
情况
|
隐式解包可选类型为
nil
导致崩溃
|
声明为隐式解包可选类型的变量在使用时为
nil
|
确保隐式解包可选类型的变量在使用前有非
nil
值,或者避免使用隐式解包可选类型
|
5. 深入理解可选类型和控制语句的重要性
可选类型和控制语句是 Swift 编程中非常重要的概念。它们不仅能够帮助我们编写更安全、更健壮的代码,还能提高代码的可读性和可维护性。
-
安全性
:可选类型通过明确表示一个变量可能没有值,避免了在其他语言中常见的
null引用错误。控制语句则允许我们根据不同的条件执行不同的代码,确保程序的逻辑正确性。 -
可读性
:合理使用可选类型和控制语句可以使代码的意图更加清晰。例如,使用
if let语法解包可选类型,能够让阅读代码的人一眼看出代码在处理可能为nil的情况。 - 可维护性 :当代码需要修改或扩展时,良好的可选类型和控制语句的使用可以减少错误的发生,使代码更容易理解和修改。
6. 总结与展望
通过本文的介绍,我们深入了解了 Swift 中的可选类型和控制语句。可选类型为我们提供了一种安全的方式来处理可能没有值的情况,而控制语句则为我们提供了丰富的编程逻辑实现手段。
在实际编程中,我们应该充分利用可选类型和控制语句的特性,编写安全、可读、可维护的代码。同时,随着对 Swift 语言的深入学习,我们还可以探索更多高级的用法和技巧,进一步提升编程能力。
以下是一个流程图,展示了使用
if let
解包可选类型的过程:
graph TD;
A[定义可选类型变量] --> B{变量是否为 nil};
B -- 是 --> C[不执行代码块];
B -- 否 --> D[解包变量并赋值给常量/变量];
D --> E[执行代码块];
为了方便回顾,我们再次列出 Swift 中处理可选类型的三种方法:
1.
检查是否为
nil
再解包
if color != nil {
let actualColor = color!
}
-
使用
if let语法
if let actualColor = color {
// Executed only if color was not nil
println(actualColor)
}
-
使用合并运算符
??
let dict = [0: "Red", 1: "Green", 2: "Blue"]
let color = dict[4]
let actualColor = color ?? "Blue"
通过不断练习和实践,我们可以更加熟练地运用可选类型和控制语句,编写出高质量的 Swift 代码。
超级会员免费看
955

被折叠的 条评论
为什么被折叠?



