Skip to content

第二讲 · Swift 进阶与 CodeBreaker 实战

本讲从语言进阶概念出发,串到真实项目代码:MVVM、类型系统、闭包、状态更新与匹配算法。

学习重心

把“会写代码”升级为“能组织结构并解释设计”。

核心能力

值类型思维、状态驱动 UI、可维护算法实现。

项目主线

围绕 CodeBreaker:从 Model 到交互再到反馈计算。

课后方向

模式扩展、参数化重构、视觉风格升级。

建议阅读方式: 先看每节“结论”,再看代码与图片。遇到疑问时回到第一讲对照基础概念。

目录

  1. 架构设计回顾:MVVM 模式
  2. Swift 类型系统复习:Struct / Class、Enum / Optional
  3. 从函数到闭包(Closure)
  4. CodeBreaker Model 设计
  5. 交互逻辑:mutating@State
  6. 核心算法:匹配逻辑(Exact / Inexact)
  7. UI 完善与工程化细节
  8. 课后任务(必做 + 挑战)
  9. 实现约束与检查清单

1) 架构设计回顾:MVVM 模式

1

把界面和规则拆开,是可维护性的起点

  • Model: 数据与规则(例如密码、判定逻辑、历史记录)。
  • View: 界面展示(颜色块、按钮、布局)。
  • ViewModel: 连接 View 与 Model,处理“用户操作 -> 状态更新”。

本项目规模不大,可以简化实现,但思维上仍要坚持关注点分离。

MVVM 分层讲解图 1:MVVM 的关键是职责拆分,不是为了“套模式”而套模式。

2) Swift 类型系统复习:Struct / Class、Enum / Optional

2

Struct vs Class:值语义与引用语义

`struct` 是值类型,赋值时拷贝;`class` 是引用类型,多个变量可指向同一对象。能用 Struct 时优先 Struct,尤其适合状态与数据模型。

Struct 与 Class 对比图 2:值类型更安全,引用类型更灵活,关键看是否需要共享可变状态。
swift
struct Resolution {
    var width = 0
    var height = 0
}

class VideoMode {
    var resolution: Resolution
    var interlaced: Bool

    init(resolution: Resolution, interlaced: Bool) {
        self.resolution = resolution
        self.interlaced = interlaced
    }
}
3

Enum 与 Optional:表达状态,不赌运气

枚举不仅能列选项,还能携带关联值。Optional 代表“可能为空”,请优先 `if let` 安全解包,避免强制解包崩溃。

Enum 与 Optional 示例图 3:枚举让状态语义化,Optional 让空值处理显式化。
swift
enum Menu {
    case hamburger
    case coke(sugar: Int)
}

if let safeValue = maybeValue {
    print(safeValue)
}

3) 从函数到闭包(Closure)

4

闭包简写不是炫技,是降低噪音

在不损失可读性的前提下,闭包可逐步简化:完整参数写法 -> 类型推断 -> `$0` 隐式参数。

Closure 简化过程图 4:闭包演进重点是“保留语义,减少样板代码”。
swift
// 完整写法
let exactCount1 = matches.filter { match in
    match == .exact
}.count

// 简写
let exactCount2 = matches.filter { $0 == .exact }.count

4) CodeBreaker Model 设计

本讲把游戏逻辑收敛到 Model,View 负责展示,避免把业务细节堆到界面层。

swift
typealias Peg = Color

struct CodeBreaker {
    var masterCode = Code(kind: .master)
    var guessCode = Code(kind: .guess)
    var attempts: [Code] = []
    let pegChoices: [Peg] = [.blue, .red, .green, .yellow]
}

struct Code {
    var pegs: [Peg] = Array(repeating: .clear, count: 4)
    var kind: Kind

    enum Kind: Equatable {
        case master
        case guess
        case attempt(matches: [Match])
    }
}

5) 交互逻辑:mutating@State

5

数据要改得动,界面要跟得上

  • mutating:允许值类型方法修改自身属性。
  • @State:告诉 SwiftUI 这是会驱动界面刷新的状态。
mutating 与 State图 5:值类型修改与界面响应要配套,否则会出现“数据变了但UI不刷新”。
swift
mutating func changeGuessPeg(at index: Int) {
    let existingPeg = guessCode.pegs[index]
    if let currentIndex = pegChoices.firstIndex(of: existingPeg) {
        let nextIndex = (currentIndex + 1) % pegChoices.count
        guessCode.pegs[index] = pegChoices[nextIndex]
    } else {
        guessCode.pegs[index] = pegChoices.first ?? .clear
    }
}

6) 核心算法:匹配逻辑(Exact / Inexact)

6

两阶段判定:先精确,再模糊

先处理位置和颜色都正确(Exact),并从待匹配序列中移除;再处理颜色对但位置错(Inexact)。删除元素时使用倒序遍历,避免索引错乱。

CodeBreaker 匹配算法图 6:匹配算法的关键是“先精确扣除,再模糊匹配”。
swift
func match(against otherCode: Code) -> [Match] {
    var results = Array(repeating: Match.noMatch, count: pegs.count)
    var pegsToMatch = otherCode.pegs

    for index in pegs.indices.reversed() {
        if pegsToMatch[index] == pegs[index] {
            results[index] = .exact
            pegsToMatch.remove(at: index)
        }
    }

    // 第二阶段:在剩余元素中处理 inexact
    return results
}

7) UI 完善与工程化细节

  • 历史记录使用 `ScrollView`,避免内容超出屏幕。
  • 用 `indices.reversed()` 控制最近一次尝试显示顺序。
  • 提交动作配合 `withAnimation` 提升反馈感。
  • 透明区域用 `.contentShape(Rectangle())` 提高可点性。
  • 随机初始化题目,提升重复游玩体验。

8) 课后任务(必做 + 挑战)

必做任务

  • 复现课堂 Lecture 1-4 功能,确保可运行。
  • 忽略无效猜测(空输入、重复尝试)。
  • 新增 Restart 并随机 Peg 数量(3~6)。
  • 支持颜色模式与 Emoji 模式随机切换。

界面任务

  • 让 Emoji 在不同 Peg 数量下可读(缩放策略)。
  • 加入 iOS 风格液态玻璃视觉(Material + 高光)。
  • 深色模式下保持边界与对比度清晰。

挑战任务

  • 扩展颜色库并做字符串到颜色映射。
  • 主题系统(车辆 / 脸部 / 大地色等)。
  • 界面显示当前主题名称。

工程要求

  • 代码命名清晰、无警告、可读性优先。
  • 至少使用一次 `static` 成员。
  • Model 层保持 UI 无关,减少 `SwiftUI` 依赖。

9) 实现约束与检查清单

  • 状态建模优先 `enum`,不要散落魔法数字。
  • ForEach 需稳定 id(`indices` 或 `Identifiable`)。
  • 涉及可变状态时,明确 `mutating` 与 `@State` 边界。
  • 算法代码先追求正确性,再谈短写法。

第二讲核心收获

你需要形成“结构化开发”的习惯:先定职责,再写状态,再落算法,最后打磨交互与视觉。这样项目才会越做越稳。

本站点基于 MIT License 发布