69、Swift 编程基础:可选类型与控制语句详解

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!
}
  1. 使用 if let 语法
if let actualColor = color {
    // Executed only if color was not nil
    println(actualColor)
}
  1. 使用合并运算符 ??
let dict = [0: "Red", 1: "Green", 2: "Blue"]
let color = dict[4]
let actualColor = color ?? "Blue"

通过不断练习和实践,我们可以更加熟练地运用可选类型和控制语句,编写出高质量的 Swift 代码。

【3D应力敏感度分析拓扑优化】【基于p-范数全局应力衡量的3D敏感度分析】基于伴随方法的有限元分析和p-范数应力敏感度分析(Matlab代码实现)内容概要:本文档介绍了基于伴随方法的有限元分析p-范数全局应力衡量的3D应力敏感度分析,并结合拓扑优化技术,提供了完整的Matlab代码实现方案。该方法通过有限元建模计算结构在载荷作用下的应力分布,采用p-范数对全局应力进行有效聚合,避免传统方法中应力约束过多的问题,进而利用伴随法高效求解设计变量对应力的敏感度,为结构优化提供关键梯度信息。整个流程涵盖了从有限元分析、应力评估到敏感度计算的核心环节,适用于复杂三维结构的轻量化高强度设计。; 适合人群:具备有限元分析基础、拓扑优化背景及Matlab编程能力的研究生、科研人员工程技术人员,尤其适合从事结构设计、力学仿真多学科优化的相关从业者; 使用场景及目标:①用于实现高精度三维结构的应力约束拓扑优化;②帮助理解伴随法在敏感度分析中的应用原理编程实现;③服务于科研复现、论文写作工程项目中的结构性能提升需求; 阅读建议:建议读者结合有限元理论优化算法知识,逐步调试Matlab代码,重点关注伴随方程的构建p-范数的数值处理技巧,以深入掌握方法本质并实现个性化拓展。
下载前必看:https://pan.quark.cn/s/9f13b242f4b9 Android 平板设备远程操控个人计算机的指南 Android 平板设备远程操控个人计算机的指南详细阐述了如何运用 Splashtop Remote 应用程序达成 Android 平板设备对个人计算机的远程操控。 该指南被划分为四个环节:首先,在个人计算机上获取并部署 Splashtop Remote 应用程序,并设定客户端密码;其次,在 Android 平板设备上获取并部署 Splashtop Remote 应用程序,并之建立连接至个人计算机的通道;再次,在 Splashtop Remote 应用程序中识别已部署个人计算机端软件的设备;最后,运用平板设备对个人计算机实施远程操控。 关键点1:Splashtop Remote 应用程序的部署配置* 在个人计算机上获取并部署 Splashtop Remote 应用程序,可通过官方网站或其他获取途径进行下载。 * 部署结束后,必须输入客户端密码,该密码在平板控制计算机时用作验证,密码长度至少为8个字符,且需包含字母数字。 * 在配置选项中,能够设定是否在设备启动时自动运行客户端,以及进行互联网搜索设置。 关键点2:Splashtop Remote 应用程序的 Android 版本获取部署* 在 Android 平板设备上获取并部署 Splashtop Remote 应用程序,可通过 Google Play Store 或其他获取途径进行下载。 * 部署结束后,必须输入客户端密码,该密码用于连接至个人计算机端软件。 关键点3:运用 Splashtop Remote 远程操控个人计算机* 在 Splashtop Remote 应用程序中识别...
先看效果: https://pan.quark.cn/s/7baef05d1d08 在信息技术范畴内,语音识别是一项核心的技术,它赋予计算机或设备解析和处理人类语音输入的能力。 本研究项目运用了MFCC(Mel Frequency Cepstral Coefficients)VQ(Vector Quantization)算法,借助VC++6.0的MFC(Microsoft Foundation Classes)库,开发出一个图形用户界面(GUI),从而达成基础的语音识别功能。 接下来将具体分析这些技术及其应用。 **MFCC特征提取**MFCC是语音信号处理中的一个标准方法,用于将复杂的语音波形转换成一组便于处理的数据参数。 MFCC模拟人类听觉系统对声音频率的感知模式,通过梅尔滤波器组对声音频谱进行分段处理,进而计算每个滤波器组的倒谱系数。 该过程包含以下环节:1. **预加重**:旨在削弱人声的低频响应部分,同时增强高频成分的强度。 2. **分帧和窗函数**:将语音信号分割成多个短时帧,并应用窗函数以降低帧帧之间的相互干扰。 3. **梅尔尺度滤波**:采用梅尔滤波器组对每一帧进行剖析,获取梅尔频率谱。 4. **取对数**:鉴于人耳对声音强度的感知呈现非线性特征,因此对梅尔频率谱取对数操作以更好地符合人类听觉系统。 5. **离散余弦变换(DCT)**:对对数谱实施DCT运算,提取主要特征,通常选取前12-20个系数作为MFCC特征。 6. **动态特性**:为了捕捉语音的时域变化特征,还可计算MFCC特征的差分值和二阶差分值。 **VQ识别算法**VQ是一种数据压缩方法,在语音识别领域中常用于特征矢量的量化处理。 其基本理念是将高维度的MFCC特征向量映射到一个小型、预...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值