学习 Swift 编程语言:新手入门教程 – wiki基地


学习 Swift 编程语言:新手入门教程

欢迎来到 Swift 的世界!如果你渴望开发 Apple 平台的应用(iOS、macOS、watchOS、tvOS),或者对一门现代、强大且安全的编程语言感兴趣,那么 Swift 是一个绝佳的选择。本教程旨在为你提供一个全面而详尽的入门指南,带你从零开始认识并掌握 Swift 的基础知识。

学习任何新的编程语言都像是一场旅程,可能会遇到挑战,但也充满了发现和创造的乐趣。请保持耐心,一步一个脚印地前进。让我们开始这段激动人心的编程之旅吧!

第一章:认识 Swift – 为什么选择它?

1.1 什么是 Swift?

Swift 是 Apple 于 2014 年推出的一门强大的开源编程语言。它被设计成一种用于构建各种应用(从手机 App 到服务器程序,从脚本到桌面应用)的通用编程语言。Swift 吸取了 C、Objective-C、Rust、Haskell、Ruby、Python 和 CLU 等语言的优点,同时避免了某些历史包袱,创造出一种既易于阅读和编写,又具备高性能和安全性的语言。

1.2 Swift 的核心特性

  • 安全 (Safe): Swift 在设计时就非常强调安全性。它消除了许多常见的编程错误,例如空指针引用(通过引入 Optional 类型)和缓冲区溢出。其严格的类型系统和错误处理机制有助于在编译时捕获错误,而不是在运行时导致崩溃。
  • 快速 (Fast): Swift 的性能堪比 C++,特别是在复杂的计算任务中。Apple 持续优化 Swift 编译器,使其能够生成高效的机器码。
  • 富有表现力 (Expressive): Swift 语法简洁、清晰,易于阅读和理解。它支持现代编程范式,如面向对象、函数式编程和协议导向编程,允许开发者用更少的代码表达复杂的逻辑。
  • 交互性 (Interactive): Swift Playgrounds 是一个革命性的工具,允许你实时编写和运行 Swift 代码,并立即看到结果。这使得学习、原型开发和实验变得非常高效。
  • 开源 (Open Source): Swift 于 2015 年开源,拥有一个活跃的社区。这意味着 Swift 可以在 Apple 平台之外运行,例如 Linux,并且其发展由 Apple 和社区共同推动。

1.3 Swift 能做什么?

Swift 最为人熟知的应用是构建 Apple 设备上的原生 App:

  • iOS App: 运行在 iPhone 和 iPad 上。
  • macOS App: 运行在 Mac 电脑上。
  • watchOS App: 运行在 Apple Watch 上。
  • tvOS App: 运行在 Apple TV 上。

但 Swift 的用途远不止于此:

  • 服务器端开发: 使用像 Vapor 或 Kitura 这样的框架构建高性能的后端服务。
  • 脚本: 编写用于自动化任务的脚本。
  • 跨平台开发: 虽然目前主要流行于 Apple 平台,但其开源性质正在推动其在其他平台上的应用。

第二章:准备环境 – 安装 Xcode 与 Playgrounds

开始学习 Swift 最简单和最推荐的方式是使用 Apple 的集成开发环境 (IDE) – Xcode。Xcode 提供了编写、运行、调试 Swift 代码所需的一切工具。

2.1 安装 Xcode

如果你使用的是 Mac 电脑,可以直接从 Mac App Store 免费下载 Xcode。搜索 “Xcode” 并点击“获取”或“下载”按钮。Xcode 的下载文件比较大(可能超过 10GB),下载和安装需要一些时间,请耐心等待。

注意: Xcode 只能安装在 macOS 系统上。如果你没有 Mac 电脑,学习 Swift 原生 App 开发会非常困难。不过,你仍然可以在 Linux 或 Windows (通过虚拟机或 WSL2 安装 Linux) 上安装 Swift 工具链进行学习,但将无法直接构建和测试 Apple App。本教程主要基于 Xcode 环境。

2.2 认识 Xcode Playgrounds

Xcode Playgrounds 是一个极好的学习和实验 Swift 代码的工具。它允许你实时输入代码,并在侧边栏即时看到代码的执行结果或变量的值。对于新手来说,Playgrounds 是探索 Swift 语法和概念的理想场所。

如何创建一个 Playground:

  1. 打开 Xcode。
  2. 在欢迎界面选择 “Create a new Xcode project”,或者从菜单栏选择 File -> New -> Playground...
  3. 在模板选择器中,选择 iOSmacOS 下的 Blank 模板,然后点击 Next
  4. 给你的 Playground 命名,选择保存位置,然后点击 Create

一个新的 Playground 窗口就会打开。你会看到一些默认的代码:

“`swift
import UIKit // 或者 import Cocoa (取决于你选择的平台)

var greeting = “Hello, playground”

// 在这里写下你的 Swift 代码
“`

你可以在 var greeting = "Hello, playground" 之后开始输入自己的代码,并在右侧的结果区域看到输出。

第三章:Swift 基础 – 变量、常量、数据类型与运算符

3.1 变量与常量

在编程中,我们需要存储和处理数据。变量和常量就是用于存储数据的“容器”。

  • 常量 (Constants):let 关键字声明。它们的值一旦被赋值后就不能再改变。如果某个值在程序运行过程中不会变动,强烈建议使用常量,因为这能提高代码的安全性和清晰度。

    swift
    let maximumNumberOfLoginAttempts = 10 // 登录尝试的最大次数
    let pi = 3.14159 // 圆周率
    let tutorialName = "Swift Basics" // 教程名称

  • 变量 (Variables):var 关键字声明。它们的值可以在程序运行过程中多次改变。

    swift
    var currentLoginAttempt = 0 // 当前登录尝试次数
    var welcomeMessage = "Hello" // 欢迎消息

Playground 实践:

在你的 Playground 中尝试声明一些变量和常量,并尝试修改变量的值,看看会发生什么。

“`swift
let language = “Swift”
var version = 5.9 // 假设当前是 Swift 5.9

// language = “Python” // 尝试修改常量会报错!
version = 6.0 // 可以修改变量

print(language) // 在控制台打印输出
print(version)
“`

你会在右侧看到 language 的值是 “Swift”,version 的值先是 5.9,然后更新为 6.0。print() 函数用于将内容输出到控制台区域。

3.2 类型推断与类型安全

Swift 是一门类型安全的语言。这意味着它在编译时就会检查你的代码,确保你不会不小心把一个 String 类型的值当作 Int 类型来使用。这有助于避免许多运行时错误。

Swift 具备强大的类型推断能力。在很多情况下,你不需要明确指定变量或常量的类型,编译器可以根据你赋给它的初始值自动推断出类型。

swift
let meaningOfLife = 42 // Swift 推断为 Int 类型
let piValue = 3.14159 // Swift 推断为 Double 类型
let isLoggedIn = true // Swift 推断为 Bool 类型
let greetingText = "Hello, Swift!" // Swift 推断为 String 类型

当然,你也可以显式地指定类型,这在你不想提供初始值或者需要指定一个不同于默认推断的类型时很有用:

“`swift
let albumTitle: String = “Fearless” // 显式指定为 String
var yearPublished: Int // 显式指定为 Int,但尚未赋值
yearPublished = 2008 // 赋值

// var population: Int = “ten million” // 错误:不能将 String 赋值给 Int
“`

显式指定类型的语法是 变量/常量名称: 类型名称

3.3 基本数据类型

Swift 提供了多种内置的数据类型来表示不同种类的数据:

  • 整型 (Integers): 用于表示整数。

    • Int: 平台的默认字长整数类型 (在 32 位系统上是 Int32,在 64 位系统上是 Int64)。通常情况下使用 Int 就足够了。
    • UInt: 无符号整型 (非负)。不常用,除非你有特别的理由需要无符号整数。
    • 还有特定字长的类型,如 Int8, Int16, Int32, Int64 和对应的 UInt 类型。
      swift
      let age: Int = 30
      let year: Int32 = 2024
  • 浮点型 (Floating-Point Numbers): 用于表示小数。

    • Double: 表示 64 位浮点数。精度更高,通常是首选。
    • Float: 表示 32 位浮点数。精度较低。
      swift
      let price: Double = 19.99
      let temperature: Float = 36.6

      注意: Swift 要求进行明确的类型转换才能混合使用不同类型的数字(例如 Int 和 Double)。

    swift
    let count = 3
    let pricePerItem = 10.5
    // let total = count * pricePerItem // 错误:Int 和 Double 不能直接相乘
    let total = Double(count) * pricePerItem // 正确:将 count 转换为 Double

  • 布尔型 (Booleans): 用于表示真或假。

    • Bool: 只有两个可能的值:truefalse
      swift
      let isComplete: Bool = true
      let hasError: Bool = false
  • 字符串 (Strings): 用于表示文本。

    • String: Swift 的 String 类型功能强大且灵活。
      swift
      let greeting = "Hello, World!"
      let emptyString = "" // 空字符串

      字符串可以通过 + 运算符进行拼接:
      swift
      let part1 = "Swift "
      let part2 = "Programming"
      let fullString = part1 + part2 // "Swift Programming"

      字符串插值是更方便的方式,用 \() 将变量或表达式嵌入字符串中:
      swift
      let name = "Alice"
      let age = 25
      let info = "My name is \(name) and I am \(age) years old." // "My name is Alice and I am 25 years old."
  • 字符 (Characters): 用于表示单个字符。

    • Character:
      swift
      let exclamationMark: Character = "!"

      请注意,字符串和字符是不同的类型,单个字符字面量需要明确指定类型或上下文能推断出它是 Character。

3.4 运算符

Swift 支持大多数标准的运算符:

  • 算术运算符: + (加), - (减), * (乘), / (除), % (取余)
    swift
    let sum = 10 + 5 // 15
    let difference = 20 - 7 // 13
    let product = 4 * 6 // 24
    let division = 10 / 2 // 5 (对于整型是整除)
    let remainder = 10 % 3 // 1
    let floatingDivision = 10.0 / 3.0 // 3.333... (对于浮点型是精确除法)

  • 赋值运算符: = (赋值)
    swift
    var x = 10

  • 复合赋值运算符: +=, -=, *=, /=, %=
    swift
    var count = 5
    count += 3 // 等价于 count = count + 3,现在 count 是 8
    count *= 2 // 等价于 count = count * 2,现在 count 是 16

  • 比较运算符: == (等于), != (不等于), > (大于), < (小于), >= (大于等于), <= (小于等于)
    swift
    let a = 10
    let b = 20
    let isEqual = (a == b) // false
    let isGreater = (a > b) // false
    let isNotEqual = (a != b) // true

    比较运算符的结果是布尔值 (truefalse)。

  • 逻辑运算符: && (逻辑与), || (逻辑或), ! (逻辑非)
    “`swift
    let isSunny = true
    let isWarm = false

    let goToBeach = isSunny && isWarm // false (必须都为 true)
    let stayInside = !isSunny || !isWarm // true (不晴朗 或者 不温暖)
    “`

  • 范围运算符:

    • 闭区间 ...: 包括上下边界。
    • 半开区间 ..<: 包括下边界,但不包括上边界。
      swift
      let range1 = 1...5 // 包含 1, 2, 3, 4, 5
      let range2 = 1..<5 // 包含 1, 2, 3, 4

      范围运算符在循环和集合操作中非常有用。

3.5 注释

注释是写给开发者看的,不会被编译器执行。它们用于解释代码的功能、目的或注意事项。

  • 单行注释:// 开头。
    swift
    // 这是一个单行注释
    let x = 10 // 这里声明了一个变量 x

  • 多行注释:/* 开头,以 */ 结尾。
    swift
    /*
    这是一个多行注释,
    可以跨越多行来解释一段代码块。
    */

    Swift 的多行注释支持嵌套,这在注释掉一段包含多行注释的代码时非常有用。

第四章:控制流 – 条件语句与循环

控制流语句允许你控制代码的执行顺序,决定哪些代码块应该被执行,以及应该执行多少次。

4.1 条件语句 (if, else if, else)

if 语句用于基于一个布尔条件执行代码。

“`swift
let temperature = 25

if temperature > 30 {
print(“天气很热!”)
} else if temperature > 20 {
print(“天气温和。”)
} else {
print(“天气有点冷。”)
}
// 输出:”天气温和。”
``
你可以使用
else if来检查多个条件,使用else来处理所有不满足之前条件的情况。条件表达式必须返回一个Bool` 值。

4.2 开关语句 (switch)

switch 语句提供了一种更清晰的方式来处理有多个可能分支的情况。它会尝试匹配一个值与多个模式(cases),并执行第一个匹配到的模式的代码块。

“`swift
let dayOfWeek = “Monday”

switch dayOfWeek {
case “Monday”:
print(“新的一周开始了!”)
case “Friday”:
print(“周末快到了!”)
case “Saturday”, “Sunday”: // 可以匹配多个值
print(“是周末!”)
default: // 必须包含 default 或覆盖所有可能的 case
print(“是工作日。”)
}
// 输出:”新的一周开始了!”
``switch语句在 Swift 中非常强大和灵活,可以匹配各种类型的值,包括区间、元组等。与 C/C++ 不同,Swift 的switch默认不会“贯穿”(fallthrough),执行完匹配的 case 后会自动跳出switch语句,除非你使用fallthrough` 关键字。

4.3 循环 (for-in, while, repeat-while)

循环允许你重复执行一段代码。

  • for-in 循环: 用于遍历一个序列(如数组、字典、范围、字符串中的字符等)。

    “`swift
    // 遍历范围
    for index in 1…5 {
    print(“(index) times 5 is (index * 5)”)
    }
    / 输出:
    1 times 5 is 5
    2 times 5 is 10
    3 times 5 is 15
    4 times 5 is 20
    5 times 5 is 25
    /

    // 遍历数组 (数组的概念稍后介绍)
    let names = [“Anna”, “Alex”, “Brian”, “Jack”]
    for name in names {
    print(“Hello, (name)!”)
    }
    / 输出:
    Hello, Anna!
    Hello, Alex!
    Hello, Brian!
    Hello, Jack!
    /
    如果你不需要知道当前是第几次循环,可以使用下划线 `_` 作为循环变量名:swift
    let numberOfTimes = 3
    for _ in 1…numberOfTimes {
    print(“Hello!”)
    }
    “`

  • while 循环: 当条件为 true 时,重复执行一段代码块。在每次循环开始前检查条件。

    swift
    var i = 0
    while i < 5 {
    print("i is \(i)")
    i += 1 // 别忘了更新条件相关的变量,否则可能无限循环!
    }
    /* 输出:
    i is 0
    i is 1
    i is 2
    i is 3
    i is 4
    */

  • repeat-while 循环: 先执行一次代码块,然后在每次循环结束时检查条件。至少会执行一次。

    swift
    var j = 0
    repeat {
    print("j is \(j)")
    j += 1
    } while j < 5
    /* 输出 (与 while 循环相同,但如果初始条件不满足,也会先执行一次):
    j is 0
    j is 1
    j is 2
    j is 3
    j is 4
    */

  • breakcontinue:

    • break: 立即终止当前循环或 switch 语句的执行。
    • continue: 停止当前循环的当次迭代,跳到下一次迭代的开始。

    swift
    for k in 1...10 {
    if k == 3 {
    continue // 跳过当前次循环,继续下一次 (k=4)
    }
    if k == 7 {
    break // 终止整个循环
    }
    print(k)
    }
    /* 输出:
    1
    2
    4
    5
    6
    */

第五章:集合类型 – 数组、字典与集合

集合类型是用于存储一组相关数据的容器。Swift 提供了三种主要的集合类型:数组、字典和集合。它们都必须存储相同类型的数据(或者遵循相同协议)。

5.1 数组 (Arrays)

数组是有序的同类型元素集合。可以通过索引(从 0 开始)访问数组中的元素。

  • 创建数组:
    “`swift
    // 空数组,显式指定类型
    var someInts: [Int] = []
    print(“someInts is of type [Int] with (someInts.count) items.”) // 输出: someInts is of type [Int] with 0 items.

    // 创建一个包含默认值的数组
    var threeDoubles = Array(repeating: 0.0, count: 3) // [0.0, 0.0, 0.0]

    // 使用数组字面量创建数组 (常用)
    var shoppingList = [“Eggs”, “Milk”] // Swift 推断为 [String]
    let favoriteGenres: [String] = [“Rock”, “Classical”, “Hip hop”] // 显式指定类型
    “`

  • 访问和修改数组:
    “`swift
    print(“The shopping list contains (shoppingList.count) items.”) // 输出: The shopping list contains 2 items.

    if shoppingList.isEmpty {
    print(“The shopping list is empty.”)
    } else {
    print(“The shopping list is not empty.”) // 输出: The shopping list is not empty.
    }

    shoppingList.append(“Flour”) // 添加新元素到末尾 [“Eggs”, “Milk”, “Flour”]
    shoppingList += [“Baking Powder”] // 使用 += 添加一个数组 [“Eggs”, “Milk”, “Flour”, “Baking Powder”]
    shoppingList += [“Chocolate Spread”, “Cheese”, “Butter”] // [“Eggs”, “Milk”, “Flour”, “Baking Powder”, “Chocolate Spread”, “Cheese”, “Butter”]

    var firstItem = shoppingList[0] // 访问第一个元素 “Eggs”
    shoppingList[0] = “Six eggs” // 修改第一个元素 [“Six eggs”, “Milk”, …]

    // 通过范围修改多个元素
    shoppingList[4…6] = [“Bananas”, “Apples”] // [“Six eggs”, “Milk”, “Flour”, “Baking Powder”, “Bananas”, “Apples”] – 数组大小会改变

    shoppingList.insert(“Maple Syrup”, at: 0) // 在指定索引插入 [“Maple Syrup”, “Six eggs”, …]

    let mapleSyrup = shoppingList.remove(at: 0) // 移除指定索引的元素,返回被移除的元素 [“Six eggs”, …]
    let lastItem = shoppingList.removeLast() // 移除并返回最后一个元素

    print(shoppingList) // 打印当前数组
    “`

  • 遍历数组: 使用 for-in 循环。
    “`swift
    for item in shoppingList {
    print(item)
    }

    // 如果需要同时访问索引和值
    for (index, value) in shoppingList.enumerated() {
    print(“Item (index + 1): (value)”)
    }
    “`

5.2 字典 (Dictionaries)

字典是无序的同类型键值对集合。每个值都与一个唯一的键关联。键必须是可哈希的(Hashable),如 String, Int, Double, Bool 等。

  • 创建字典:
    “`swift
    // 空字典,显式指定键值类型
    var namesOfIntegers: [Int: String] = [:]
    // namesOfIntegers[16] = “sixteen” // 添加键值对
    // print(namesOfIntegers) // [16: “sixteen”]

    // 使用字典字面量创建字典 (常用)
    var airports: [String: String] = [“YYZ”: “Toronto Pearson”, “LHR”: “London Heathrow”] // 键是 String,值是 String
    let colors = [“red”: “FF0000”, “green”: “00FF00”, “blue”: “0000FF”] // Swift 推断为 [String: String]
    “`

  • 访问和修改字典:
    “`swift
    print(“The airports dictionary contains (airports.count) items.”) // 输出: The airports dictionary contains 2 items.

    if airports.isEmpty {
    print(“The airports dictionary is empty.”)
    } else {
    print(“The airports dictionary is not empty.”) // 输出: The airports dictionary is not empty.
    }

    airports[“LHR”] = “London Heathrow International” // 更新键为 “LHR” 的值
    airports[“EDI”] = “Edinburgh” // 添加新的键值对

    let airportName = airports[“YYZ”] // 访问键为 “YYZ” 的值。结果是 Optional String (“Toronto Pearson” 或 nil)
    // 注意:字典访问返回的是 Optional 类型,因为键可能不存在。稍后会详细讲解 Optional。
    if let airportName = airports[“YYZ”] {
    print(“The name of the airport is (airportName).”) // 输出: The name of the airport is Toronto Pearson.
    } else {
    print(“That airport is not in the airports dictionary.”)
    }

    // updateValue(_:forKey:) 方法:如果键存在则更新并返回旧值,否则添加新键值对并返回 nil。
    if let oldValue = airports.updateValue(“Dublin Airport”, forKey: “DUB”) {
    print(“The old value for DUB was (oldValue).”) // 不会执行,因为 DUB 不存在
    } else {
    print(“DUB was not in the dictionary.”) // 输出: DUB was not in the dictionary.
    }
    airports.updateValue(“Dublin Airport”, forKey: “DUB”) // 添加 DUB

    // 移除键值对
    airports[“EDI”] = nil // 将键的值设置为 nil 来移除
    if let removedValue = airports.removeValue(forKey: “DUB”) {
    print(“The removed airport name is (removedValue).”) // 输出: The removed airport name is Dublin Airport.
    } else {
    print(“The airports dictionary does not contain a value for DUB.”)
    }

    print(airports)
    “`

  • 遍历字典: 使用 for-in 循环遍历键值对。
    “`swift
    for (airportCode, airportName) in airports {
    print(“(airportCode): (airportName)”)
    }

    // 只遍历键
    for airportCode in airports.keys {
    print(“Key: (airportCode)”)
    }

    // 只遍历值
    for airportName in airports.values {
    print(“Value: (airportName)”)
    }
    “`

5.3 集合 (Sets)

集合是无序的同类型唯一值集合。集合常用于快速判断某个元素是否已经存在,或者进行集合运算(如并集、交集)。集合中的元素必须是可哈希的。

  • 创建集合:
    “`swift
    // 空集合,显式指定类型
    var favoriteGenresSet: Set = []

    // 使用数组字面量创建集合 (注意需要显式指定类型,否则会被推断为数组)
    var colorsSet: Set = [“red”, “green”, “blue”]
    print(“colorsSet has (colorsSet.count) items.”) // 输出: colorsSet has 3 items.

    // 包含重复元素的数组字面量,创建时重复元素会被忽略
    var uniqueNumbers: Set = [1, 2, 3, 2, 1]
    print(uniqueNumbers) // 输出: {1, 2, 3} (顺序不定)
    “`

  • 访问和修改集合:
    “`swift
    if colorsSet.isEmpty {
    print(“colorsSet is empty.”)
    } else {
    print(“colorsSet is not empty.”) // 输出: colorsSet is not empty.
    }

    // insert(_:) 方法:插入元素,如果元素已存在,返回 false,否则返回 true 和插入的元素。
    if colorsSet.insert(“yellow”).inserted {
    print(“yellow was inserted successfully.”) // 输出: yellow was inserted successfully.
    } else {
    print(“yellow is already a member.”)
    }
    print(colorsSet) // {“red”, “green”, “blue”, “yellow”} (顺序不定)

    // remove(_:) 方法:移除指定元素,如果元素存在并被移除,返回该元素;否则返回 nil。
    if let removedColor = colorsSet.remove(“blue”) {
    print(“(removedColor) was removed from colorsSet.”) // 输出: blue was removed from colorsSet.
    } else {
    print(“blue is not a member of colorsSet.”)
    }

    // contains(_:) 方法:检查集合是否包含某个元素
    if colorsSet.contains(“red”) {
    print(“colorsSet contains red.”) // 输出: colorsSet contains red.
    } else {
    print(“colorsSet does not contain red.”)
    }
    “`

  • 遍历集合: 使用 for-in 循环 (顺序不定)。
    “`swift
    for color in colorsSet {
    print(color)
    }

    // 如果需要按特定顺序遍历,可以先排序
    for color in colorsSet.sorted() {
    print(color)
    }
    “`

第六章:函数 – 组织你的代码

函数是执行特定任务的代码块,它们可以接受输入(参数)并产生输出(返回值)。使用函数可以避免代码重复,使程序更易于组织和理解。

6.1 定义与调用函数

函数的定义以 func 关键字开始。

“`swift
// 定义一个没有参数和返回值的函数
func sayHello() {
print(“Hello!”)
}

// 调用函数
sayHello() // 输出: Hello!

// 定义一个带参数的函数
func greet(person name: String) { // 这里的 person 是外部参数名,name 是内部参数名
print(“Hello, (name)!”)
}

// 调用带参数的函数
greet(person: “Alice”) // 输出: Hello, Alice!

// 定义一个带多个参数的函数
func greet(person name: String, alreadyGreeted: Bool) {
if alreadyGreeted {
print(“Hello again, (name)!”)
} else {
print(“Hello, (name)!”)
}
}
greet(person: “Bob”, alreadyGreeted: true) // 输出: Hello again, Bob!

// 定义一个带返回值的函数
func buildGreeting(for person: String) -> String { // -> String 表示返回一个 String 类型的值
return “Hello, ” + person + “!”
}
let greetingMessage = buildGreeting(for: “Charlie”)
print(greetingMessage) // 输出: Hello, Charlie!
“`

6.2 参数名称

Swift 中的函数参数可以有外部参数名和内部参数名。

  • 外部参数名 (External Parameter Name): 调用函数时使用的名称(默认为第一个参数名之后的所有参数名)。
  • 内部参数名 (Local Parameter Name): 在函数体内部使用的名称。

“`swift
func sendMessage(to recipient: String, message text: String) {
// recipient 和 text 是函数内部使用的名称
print(“Sending ‘(text)’ to (recipient)”)
}

// 调用时使用外部参数名
sendMessage(to: “David”, message: “How are you?”) // 输出: Sending ‘How are you?’ to David
``
默认情况下,第一个参数没有外部参数名,你可以用下划线
_` 显式省略外部参数名:

“`swift
func printNumber(_ number: Int) { // _ 表示没有外部参数名
print(“The number is (number)”)
}

printNumber(123) // 调用时不需要写参数名
“`

6.3 默认参数值

你可以在函数定义时为参数设置默认值,这样在调用函数时就可以省略该参数。

“`swift
func makeCoffee(type: String = “Cappuccino”, extraShot: Bool = false) -> String {
var order = “Making a (type)”
if extraShot {
order += ” with an extra shot”
}
order += “.”
return order
}

print(makeCoffee()) // 使用所有默认值 – 输出: Making a Cappuccino.
print(makeCoffee(type: “Latte”)) // 只指定 type – 输出: Making a Latte.
print(makeCoffee(extraShot: true)) // 只指定 extraShot (通过参数名) – 输出: Making a Cappuccino with an extra shot.
print(makeCoffee(type: “Espresso”, extraShot: true)) // 指定所有参数 – 输出: Making a Espresso with an extra shot.
“`

6.4 可变参数

函数可以接受可变数量的同类型参数。这些参数在函数体内部会作为一个该类型的数组。

“`swift
func average(_ numbers: Double…) -> Double { // … 表示可变参数
var total = 0.0
for number in numbers {
total += number
}
return total / Double(numbers.count)
}

print(average(1, 2, 3, 4, 5)) // 输出: 3.0
print(average(3.14, 2.718)) // 输出: 2.929
“`

6.5 函数类型

每个函数都有一个特定的类型,由其参数类型和返回值类型组成。例如,func addTwoInts(a: Int, b: Int) -> Int 的函数类型是 (Int, Int) -> Int。这使得函数可以作为参数传递给其他函数,或者作为函数的返回值。

“`swift
func multiplyTwoInts(a: Int, b: Int) -> Int {
return a * b
}

var mathFunction: (Int, Int) -> Int = multiplyTwoInts // mathFunction 现在引用 multiplyTwoInts 函数

print(“Result: (mathFunction(2, 3))”) // 输出: Result: 6

// 函数作为参数
func printMathResult(_ mathFunc: (Int, Int) -> Int, _ a: Int, _ b: Int) {
print(“Result: (mathFunc(a, b))”)
}

printMathResult(multiplyTwoInts, 5, 4) // 输出: Result: 20
“`

第七章:Optionals – 处理可能缺失的值

Optionals (可选值) 是 Swift 中一个非常重要的概念,它解决了许多其他语言中常见的空指针或 nil 引用问题。Optional 类型的值可能包含一个值,或者可能根本不包含值 (即它是 nil)。

7.1 nil 的问题

在许多编程语言中,允许一个变量引用“空”或“null”是一个常见的错误来源。当你尝试访问一个空引用指向的内存时,程序通常会崩溃。Swift 通过强制你处理值可能缺失的情况来避免这个问题。

7.2 Optional 类型的声明

你可以在任何类型后面加上一个问号 ? 来声明一个 Optional 类型。

“`swift
let possibleNumber = “123”
let convertedNumber = Int(possibleNumber) // convertedNumber 的类型是 Int? (Optional)
// convertedNumber 可能包含整数值 123,也可能包含 nil (如果字符串不能转换成整数)

let someString = “Hello”
let anotherNumber = Int(someString) // anotherNumber 的类型是 Int?,值为 nil
“`

一个 Optional 类型的值如果包含实际的值,你可以认为它是一个“包裹着”该值的 Optional。如果它不包含值,那么它就是 nil

7.3 强制解包 (Forced Unwrapping)

如果你确定一个 Optional 肯定包含一个值,可以使用感叹号 ! 来强制解包。

“`swift
let myNumber: Int? = 10
print(myNumber!) // 输出: 10

let absentNumber: Int? = nil
// print(absentNumber!) // 运行时错误!如果 Optional 为 nil 时强制解包会崩溃!
“`
强制解包非常危险,因为它可能导致运行时崩溃。应尽量避免在不确定 Optional 是否有值时使用强制解包。

7.4 Optional 绑定 (Optional Binding)

Optional 绑定是一种安全地检查 Optional 是否包含值,并在有值时将其提取出来赋给一个临时常量或变量的方式。

“`swift
if let actualNumber = Int(possibleNumber) { // 尝试将 convertedNumber 解包到 actualNumber 常量
print(“The string \”(possibleNumber)\” has an integer value of (actualNumber)”)
} else {
print(“The string \”(possibleNumber)\” could not be converted to an integer”)
}
// 输出: The string “123” has an integer value of 123

if let firstItem = shoppingList.first { // 访问数组的第一个元素,可能为 nil
print(“The first item is (firstItem).”)
}
``
你可以在同一个
if let` 语句中绑定多个 Optional:

swift
if let first = Int("1"), let second = Int("2") {
print("Both converted successfully: \(first) and \(second)") // 输出: Both converted successfully: 1 and 2
}

7.5 Guard 语句

guard 语句用于在函数或循环开始时检查先决条件。如果条件不满足,它会通过 else 子句退出当前作用域(例如使用 return, break, continuethrow)。它常用于 Optional 绑定,可以在满足条件时将解包的值引入当前作用域,而不仅仅是 if 语句的局部作用域。

“`swift
func greet(person: [String: String]) {
guard let name = person[“name”] else { // 检查 person 字典中是否有 “name” 键
return // 如果没有,直接退出函数
}

print("Hello \(name)!")

guard let location = person["location"] else {
    print("I hope the weather is great near you.")
    return // 如果没有 location,打印一条消息然后退出
}

print("I hope the weather is great in \(location).")

}

greet(person: [“name”: “Jane”])
/ 输出:
Hello Jane!
I hope the weather is great near you.
/

greet(person: [“name”: “John”, “location”: “Cupertino”])
/ 输出:
Hello John!
I hope the weather is great in Cupertino.
/
``guard` 语句可以提高代码的可读性,清晰地表达了“满足这些条件才能继续执行”。

7.6 Nil-Coalescing 运算符 (??)

Nil-Coalescing 运算符 (a ?? b) 用于解包一个 Optional a。如果 a 包含一个值,则使用该值;如果 anil,则使用默认值 bb 必须与 a 的类型兼容,且 b 不是 Optional。

“`swift
let defaultColorName = “red”
var userDefinedColorName: String? // 默认为 nil

var colorNameToUse = userDefinedColorName ?? defaultColorName // userDefinedColorName 是 nil,所以使用 defaultColorName
print(colorNameToUse) // 输出: red

userDefinedColorName = “green”
colorNameToUse = userDefinedColorName ?? defaultColorName // userDefinedColorName 有值,所以使用它的值
print(colorNameToUse) // 输出: green
“`

7.7 Optional Chaining (?)

Optional Chaining 是一种安全地访问 Optional 值上的属性、方法或下标的方式。如果在 Optional 为 nil 时尝试访问,整个表达式会短路并返回 nil,而不是触发运行时错误。结果仍然是一个 Optional。

“`swift
class Residence {
var numberOfRooms = 1
}

class Person {
var residence: Residence? // Person 可能有 Residence,也可能没有 (nil)
}

let john = Person()
// print(john.residence.numberOfRooms) // 错误:residence 是 Optional,不能直接访问属性

// 使用 Optional Chaining
if let roomCount = john.residence?.numberOfRooms { // 如果 residence 不是 nil,就访问 numberOfRooms
print(“John’s residence has (roomCount) room(s).”)
} else {
print(“John does not have a residence.”) // 输出: John does not have a residence.
}

john.residence = Residence() // 现在 John 有住所了
if let roomCount = john.residence?.numberOfRooms {
print(“John’s residence has (roomCount) room(s).”) // 输出: John’s residence has 1 room(s).
}
``
Optional chaining 可以串联起来:
a?.b?.c?.d。任何一个环节是nil,整个表达式的结果就是nil`。

第八章:结构体、类与枚举 – 组织你的数据

Swift 提供了三种主要的自定义数据类型来组织和建模你的数据:结构体 (Structs)、类 (Classes) 和枚举 (Enums)。

8.1 结构体 (Structs)

结构体是值类型 (Value Types)。当你复制一个结构体实例时,会创建一个完全独立的新副本。它们适用于表示相对简单的数据结构,例如二维坐标点、范围或者一些属性的集合。

“`swift
struct Point {
var x = 0.0 // 存储属性
var y = 0.0
}

struct Size {
var width = 0.0
var height = 0.0
}

struct Rect {
var origin = Point() // 存储属性,类型是 Point 结构体
var size = Size() // 存储属性,类型是 Size 结构体

// 计算属性 (Computed Property): 它不存储值,而是提供一个 getter (可选的 setter) 来计算值
var center: Point {
    get { // getter 用于获取属性值
        let centerX = origin.x + size.width / 2
        let centerY = origin.y + size.height / 2
        return Point(x: centerX, y: centerY)
    }
    set(newCenter) { // setter 用于设置属性值 (newCenter 是默认的参数名)
        origin.x = newCenter.x - size.width / 2
        origin.y = newCenter.y - size.height / 2
    }
}

}

// 创建结构体实例
let originPoint = Point(x: 0.0, y: 0.0)
let rectSize = Size(width: 10.0, height: 5.0)
var myRect = Rect(origin: originPoint, size: rectSize)

print(“My rectangle’s origin is at ((myRect.origin.x), (myRect.origin.y))”) // 输出: My rectangle’s origin is at (0.0, 0.0)
print(“My rectangle’s center is at ((myRect.center.x), (myRect.center.y))”) // 输出: My rectangle’s center is at (5.0, 2.5)

// 通过计算属性设置值
let newCenter = Point(x: 10.0, y: 10.0)
myRect.center = newCenter
print(“My rectangle’s origin is now at ((myRect.origin.x), (myRect.origin.y))”) // 输出: My rectangle’s origin is now at (5.0, 7.5)

// 结构体是值类型:复制会创建独立副本
var anotherRect = myRect
anotherRect.origin.x = 100.0 // 修改副本不会影响原实例
print(“myRect origin x: (myRect.origin.x)”) // 输出: myRect origin x: 5.0
print(“anotherRect origin x: (anotherRect.origin.x)”) // 输出: anotherRect origin x: 100.0
``
结构体默认提供一个成员wise初始化器 (
init`),可以用来创建实例并初始化所有存储属性。

8.2 类 (Classes)

类是引用类型 (Reference Types)。当你复制一个类实例时,创建的只是对同一个实例的引用,而不是一个新的副本。多个常量或变量可能引用同一个类实例。类适用于建模具有身份标识的复杂实体,例如用户、视图控制器等。

“`swift
class Human {
var name: String
var age: Int

// 初始化器 (Initializer): 用于创建类实例并设置初始值
init(name: String, age: Int) {
    self.name = name // self.name 指的是类的属性,name 指的是初始化器的参数
    self.age = age
}

// 方法 (Method): 与类实例关联的函数
func sayGreeting() {
    print("Hello, my name is \(name) and I am \(age) years old.")
}

}

// 创建类实例
let person1 = Human(name: “Alice”, age: 30)
person1.sayGreeting() // 输出: Hello, my name is Alice and I am 30 years old.

// 类是引用类型:复制引用
let person2 = person1 // person2 现在引用的是和 person1 同一个 Human 实例

person2.age = 31 // 通过 person2 修改 age

print(“person1’s age is (person1.age)”) // 输出: person1’s age is 31 (因为 person1 和 person2 引用同一个实例)
print(“person2’s age is (person2.age)”) // 输出: person2’s age is 31
``
类没有默认的成员wise初始化器,你通常需要自己定义
init` 方法。类还支持继承 (Inheritance),一个类可以继承另一个类的属性和方法。

8.3 枚举 (Enums)

枚举用于定义一组相关的、有限的可能值。它们提供了比简单整数常量更安全和更有意义的方式来表示状态或选项。

“`swift
// 基本枚举
enum CompassPoint {
case north
case south
case east
case west
}

var directionToHead = CompassPoint.west // 使用点语法访问枚举成员
directionToHead = .north // 类型已知时可以省略枚举类型名

switch directionToHead {
case .north:
print(“Heading north”) // 输出: Heading north
case .south:
print(“Heading south”)
case .east:
print(“Heading east”)
case .west:
print(“Heading west”)
}

// 带原始值的枚举 (Raw Values)
enum Planet: Int { // 指定原始值为 Int 类型
case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
}

let earth = Planet.earth
print(“Earth is planet number (earth.rawValue)”) // 访问原始值 – 输出: Earth is planet number 3

// 通过原始值创建枚举实例 (返回的是 Optional,因为可能不存在对应原始值的成员)
if let possiblePlanet = Planet(rawValue: 7) {
switch possiblePlanet {
case .earth:
print(“That’s Earth!”)
default:
print(“That’s another planet.”) // 输出: That’s another planet.
}
} else {
print(“There is no planet at that position.”)
}

// 带关联值的枚举 (Associated Values)
enum Barcode {
case upc(Int, Int, Int, Int) // 关联一个元组
case qrCode(String) // 关联一个 String
}

var productBarcode = Barcode.upc(8, 85909, 51226, 3)
productBarcode = .qrCode(“ABCDEFGHIJKLMNOP”) // 可以改变关联值和成员本身

switch productBarcode {
case .upc(let numberSystem, let manufacturer, let product, let check):
print(“UPC: (numberSystem), (manufacturer), (product), (check).”)
case .qrCode(let productCode): // 可以使用 let 或 var 提取关联值
print(“QR code: (productCode).”) // 输出: QR code: ABCDEFGHIJKLMNOP.
}
“`

8.4 结构体 vs 类

这是新手常问的问题。简单来说:

  • 使用结构体 (Structs):
    • 当你需要表示相对简单的数据结构时。
    • 当复制实例时需要一个独立的副本时(值语义)。
    • 当你需要表示一组相关的属性,且没有继承的需求时。
    • 通常,Swift 官方推荐优先使用结构体,除非有明确的理由需要类的特性(如引用语义或继承)。Swift 的许多内置类型(如 String, Array, Dictionary, Int, Bool, Double 等)都是结构体。
  • 使用类 (Classes):
    • 当你需要表示复杂的实体,并且需要引用语义时(多个引用指向同一个实例)。
    • 当你需要继承来共享代码或定义类层次结构时。
    • 当你需要与 Objective-C 代码互操作时(Objective-C 没有结构体)。

第九章:错误处理 – 如何应对失败

有些操作是无法保证一定成功的,例如从文件中读取数据、通过网络请求数据等。Swift 的错误处理机制允许你编写健壮的代码,优雅地处理这些可能失败的情况。

9.1 表示和抛出错误

在 Swift 中,遵循 Error 协议的任何类型都可以表示一个错误。通常使用枚举来定义一组相关的错误。

“`swift
enum VendingMachineError: Error {
case invalidSelection // 选择无效
case insufficientFunds(coinsNeeded: Int) // 钱不够
case outOfStock // 缺货
}

// 使用 throws 关键字表示函数可能会抛出错误
func canThrowAnError() throws {
// 这段代码可能会抛出错误
// throw VendingMachineError.invalidSelection // 抛出错误
}
“`

9.2 处理错误

有四种方式处理可能抛出错误的操作:

  1. 使用 do-catch 语句: 最常用的方式,在一个 do 块中执行可能抛出错误的代码,然后在 catch 块中捕获并处理错误。

    “`swift
    func buyFavoriteSnack(person: String, vendingMachine: VendingMachine) throws {
    let snackName = favoriteSnacks[person] ?? “Candy Bar” // 使用 nil-coalescing 运算符提供默认值
    try vendingMachine.vend(itemNamed: snackName) // 调用可能抛出错误的方法,使用 try
    }

    // 假设 VendingMachine 类和 vend 方法已经定义
    class VendingMachine {
    var inventory = [“Candy Bar”: 1, “Chips”: 1, “Pretzels”: 1]
    var coinsDeposited = 0
    let favoriteSnacks = [“Alice”: “Chips”, “Bob”: “Licorice”, “Eve”: “Pretzels”]

    func vend(itemNamed name: String) throws {
        guard let item = inventory[name] else {
            throw VendingMachineError.invalidSelection
        }
        guard item > 0 else {
            throw VendingMachineError.outOfStock
        }
        guard coinsDeposited >= 1 else { // 假设每个商品需要 1 个硬币
             throw VendingMachineError.insufficientFunds(coinsNeeded: 1 - coinsDeposited)
        }
    
        coinsDeposited -= 1
        inventory[name] = item - 1
        print("Dispensing \(name)")
    }
    
    func deposit(_ coins: Int) {
        coinsDeposited += coins
    }
    

    }

    let vendingMachine = VendingMachine()
    vendingMachine.deposit(1) // 存入 1 个硬币

    do { // 执行可能抛出错误的代码块
    try vendingMachine.vend(itemNamed: “Candy Bar”) // 尝试购买糖果棒
    // 如果成功,这里的代码会执行
    print(“Purchase successful.”)
    } catch VendingMachineError.invalidSelection {
    print(“Invalid Selection.”)
    } catch VendingMachineError.outOfStock {
    print(“Out of Stock.”)
    } catch VendingMachineError.insufficientFunds(let coinsNeeded) { // 捕获特定错误并提取关联值
    print(“Insufficient funds. Please insert an additional (coinsNeeded) coins.”)
    } catch { // 捕获所有其他错误
    print(“An unexpected error occurred: (error)”)
    }
    // 输出:
    // Dispensing Candy Bar
    // Purchase successful.

    vendingMachine.deposit(0) // 没有存入硬币
    do {
    try vendingMachine.vend(itemNamed: “Candy Bar”) // 再次尝试购买,但缺货且钱不够
    } catch VendingMachineError.insufficientFunds(let coinsNeeded) {
    print(“Insufficient funds. Please insert an additional (coinsNeeded) coins.”) // 捕获并处理
    } catch { // 捕获其他错误 (如 outOfStock 或 invalidSelection)
    print(“Another error occurred: (error)”)
    }
    // 输出: Insufficient funds. Please insert an additional 1 coins.
    ``
    你可以在
    catch后面指定要捕获的错误类型。如果没有指定,则会捕获所有错误,并自动提供一个名为error` 的局部常量。

  2. 使用 try? 将错误转换为 Optional: 如果你只关心操作是否成功(而不关心具体的错误原因),可以使用 try?。如果操作抛出错误,结果就是 nil;如果成功,结果就是一个包含返回值的 Optional。

    “`swift
    // 假设 someThrowingFunction() 会抛出错误或者返回一个 Int
    let x = try? someThrowingFunction() // x 是 Optional

    // 可以这样判断是否成功
    if let actualX = try? someThrowingFunction() {
    print(“Function succeeded with value (actualX).”)
    } else {
    print(“Function failed.”)
    }
    “`

  3. 使用 try! 强制禁用错误传播: 如果你绝对确定某个操作不会抛出错误,可以使用 try!。这会强制解包操作的结果。如果操作实际上抛出了错误,程序会立即崩溃。谨慎使用,仅在你 100% 确定不会出错时使用。

    swift
    // 假设 imageData(fromFile:) 绝对不会因为给定的文件名抛出错误
    // 例如,你知道文件肯定存在且格式正确
    let photo = try! imageData(fromFile: "./Resources/photo.jpg") // photo 是 Data 类型

  4. 在抛出函数中处理错误: 如果你的函数本身就声明为 throws,你可以在其中调用其他抛出错误的函数,而无需使用 do-catch(除非你需要处理特定的错误并继续执行)。错误会从你的函数继续向上传播。

    swift
    func processFile(filename: String) throws {
    // 这里可以调用 try readFile(named: filename)
    // 如果 readFile 抛出错误,processFile 函数也会抛出同一个错误
    }

第十章:进一步学习的方向

本教程只是 Swift 的入门。Swift 还有许多更高级和强大的特性,值得你在掌握基础后深入学习:

  • Protocols (协议): 定义类型必须遵循的蓝图,是 Swift 面向协议编程 (POP) 的基石。
  • Extensions (扩展): 允许你为一个已有的类、结构体、枚举或协议添加新功能。
  • Closures (闭包): 自包含的函数块,可以在代码中被传递和使用。在异步编程、高阶函数中非常常见。
  • Generics (泛型): 编写灵活且可重用的代码,能够适用于各种类型。
  • Access Control (访问控制): 控制你的代码(类、属性、方法等)对其他源文件或模块的可见性。
  • Memory Safety & ARC: Swift 如何通过自动引用计数 (ARC) 管理内存,以及如何避免常见的内存问题。
  • Concurrency (并发): 使用 asyncawait 来编写异步和并行代码,这在 UI 编程和网络请求中非常重要。
  • 属性观察器 (Property Observers): 响应属性值的变化。
  • 下标 (Subscripts): 访问集合中元素的便捷方式 (如 Array 使用方括号 [])。
  • 运算符重载 (Operator Overloading): 为自定义类型定义运算符的行为。

更重要的是,学习 Swift 的最终目标往往是构建应用。你需要学习 Apple 提供的框架:

  • SwiftUI: 新一代的声明式 UI 框架,使用 Swift 代码直接构建跨 Apple 平台的界面。这是目前推荐的学习方向。
  • UIKit (iOS/tvOS) / AppKit (macOS): 传统的命令式 UI 框架。虽然 SwiftUI 流行,但了解 UIKit/AppKit 对维护现有项目或处理更复杂的原生功能仍然有益。
  • Foundation: 提供基本数据类型、集合、文件操作、网络等核心服务的框架。
  • Combine: 声明式响应式编程框架,用于处理事件流。
  • Core Data / Realm / SwiftData: 数据持久化技术。
  • Core Animation / SpriteKit / SceneKit: 图形和游戏开发框架。

第十一章:实践是学习的关键

阅读教程和理解概念只是第一步。真正掌握 Swift 需要大量的练习:

  • 使用 Playgrounds 实验: 尝试本教程中的所有代码示例,并修改它们,看看会发生什么。用 Playgrounds 探索新的 Swift 特性。
  • 解决编程练习: 寻找在线的 Swift 编程练习或挑战,如 LeetCode、HackerRank 等,用 Swift 实现解决方案。
  • 构建小型项目: 尝试实现一些简单的程序,例如:
    • 一个命令行计算器。
    • 一个待办事项列表(可以先用数组存储)。
    • 一个简单的文字游戏(如猜数字)。
    • 如果学习 UI 框架,尝试构建一个简单的计数器 App、一个显示列表的 App 等。
  • 阅读 Swift 官方文档: Apple 提供了非常高质量的 Swift 语言指南和 API 文档,这是最权威的学习资源。
  • 参考开源项目: 阅读其他人用 Swift 编写的代码,学习他们的风格和技巧。
  • 参与社区: 加入 Swift 论坛、Stack Overflow 或开发者社群,提问和帮助他人。

总结

恭喜你完成了这篇 Swift 新手入门教程!你已经了解了 Swift 的基本语法、核心概念、数据类型、控制流、集合、函数、Optional 和错误处理,以及结构体、类和枚举的区别。

Swift 是一门令人兴奋且功能强大的语言,它为构建现代、安全、高性能的软件提供了坚实的基础。从这里开始,你已经踏上了成为 Swift 开发者的道路。请记住,编程是一项需要持续学习和实践的技能。

不要害怕犯错,大胆地尝试和探索。编写代码,运行代码,理解错误信息,并不断改进。随着你对 Swift 越来越熟悉,你将能够构建越来越复杂的应用,实现你的创意。

祝你在 Swift 的学习旅程中一切顺利,编码愉快!

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部