WWDC Quick Look 💓 By SwiftGGTeam
A Swift Tour: Explore Swift’s features and design

A Swift Tour: Explore Swift’s features and design

观看原视频

Highlight

这是一场面向所有开发者的 Swift 语言导览,但不是简单罗列语法特性。演讲者 Allan Shortlidge 从 Swift 编译器团队的角度,围绕构建一个社交网络图数据模型,展示了 Swift 的核心设计原则如何在实际代码中发挥作用。

核心内容

写 Swift 代码时,最常见的困惑是:什么时候用 struct,什么时候用 class?Optional 和 throw 到底怎么选?并发安全到底该怎么做?这些问题的答案分散在文档各处,初学者很难形成系统认知。

这场 Session 的解法很直接:用同一个社交网络项目贯穿全程。演讲者从”一个 User 怎么表示”开始,一步步搭建出完整的图数据模型、HTTP 服务器和命令行客户端。每引入一个语言特性,都对应一个真实需求——值语义解决数据隔离、guard 语句拦截非法输入、actor 保护共享状态。

Swift 的设计哲学在这条叙事线上变得清晰:用值类型作为默认选择,用编译器强制你处理错误和空值,用类型系统在编译期排除数据竞争——这些原则互相支撑,构成一个整体。

详细内容

值语义与不可变性

Swift 用 var 声明可变变量,let 声明不可变常量。struct 是值类型,赋值时自动拷贝:

struct User {
    let username: String
    var isVisible: Bool = true
    var friends: [String] = []
}

var alice = User(username: "alice")
alice.friends = ["charlie"]

var bruno = User(username: "bruno")
bruno.friends = alice.friends

alice.friends.append("dash")
bruno.friends  // ["charlie"],不受 alice 修改影响

关键点:

  • let username 表示创建后不可修改,var friends 允许修改
  • bruno.friends = alice.friends 拷贝了整个数组,两个变量各自独立
  • struct 由值类型组合而成,自动获得值语义——这是 Swift 并发安全的基础(03:04

错误处理与 Optional

Swift 区分三种异常情况:可恢复错误用 throw,可能没有值用 Optional,程序逻辑错误用 precondition/fatalError

struct User {
    let username: String
    var isVisible: Bool = true
    public private(set) var friends: [String] = []

    mutating func addFriend(username: String) throws {
        guard username != self.username else {
            throw SocialError.befriendingSelf
        }
        guard !friends.contains(username) else {
            throw SocialError.duplicateFriend(username: username)
        }
        friends.append(username)
    }
}

enum SocialError: Error {
    case befriendingSelf
    case duplicateFriend(username: String)
}

关键点:

  • mutating 标记允许方法修改 struct 属性(03:05
  • guard 语句检测错误条件,不满足时必须退出函数
  • throws 标记函数可能抛错,调用方必须用 try 显式标记
  • SocialError.duplicateFriend(username: username) 的关联值让错误携带上下文,调用方知道”哪个用户重复了”
  • private(set) 让 friends 的读权限公开、写权限私有,强制外部通过 addFriend 修改

Optional 用于”可能没有值”的场景,Dictionary 的下标访问天然返回 Optional:

func findUser(_ username: String) -> User? {
    allUsers[username]
}

if let charlie = findUser("charlie") {
    print("Found \(charlie)")
} else {
    print("charlie not found")
}

关键点:

  • User? 是 Optional 类型,要么是 User,要么是 nil
  • if let 安全解包,只有非 nil 时才进入分支
  • 强制解包 ! 在 nil 时会崩溃,只在你 100% 确定时使用

协议与泛型

协议让抽象不依赖继承层级,泛型让算法适用于任何符合约束的类型:

extension UserStore {
    public func friendsOfFriends(_ username: String) throws -> [String] {
        let user = try user(for: username)
        let excluded = Set(user.friends + [username])
        return user.friends
            .compactMap { lookUpUser($0) }      // [String] -> [User]
            .flatMap { $0.friends }             // [User] -> [String]
            .filter { !excluded.contains($0) }  // drop excluded
            .uniqued()
    }
}

extension Collection where Element: Hashable {
    func uniqued() -> [Element] {
        let unique = Set(self)
        return Array(unique)
    }
}

关键点:

  • compactMap 过滤 nil 并解包,比 map + filter 更简洁(16:13
  • flatMap 将嵌套数组展平
  • uniqued() 是自定义扩展,通过 where Element: Hashable 约束,只对元素可哈希的 Collection 生效
  • 协议 + 泛型让这个算法适用于 Array、Set、Dictionary Keys 等任何符合条件的集合类型

并发:actor 与 Sendable

Swift 6 在编译期验证数据竞争安全。共享可变状态必须用 actor 保护:

actor UserStore {
    var allUsers: [String: User] = [:]
}

router.get("friendsOfFriends") { request, context -> [String] in
    let username = try request.queryArgument(for: "username")
    return try await UserStore.shared.friendsOfFriends(username)
}

关键点:

  • actor 是引用类型,自动序列化访问——同一时刻只有一个 Task 能执行 actor 上的方法(22:24
  • 从 actor 外部调用方法必须用 await,因为调用方可能被挂起等待
  • Sendable 协议标记可以安全跨并发域传递的类型,值类型(struct)天然更容易满足 Sendable

核心启发

  1. 用值语义建模数据:定义新类型时默认用 struct。值类型的赋值自动拷贝,天然隔离状态,在并发场景下更安全。只有需要引用语义(共享可变状态、身份、继承)时才用 class 或 actor。怎么开始:审查现有代码中的 class,如果没有共享状态需求,改成 struct。

  2. 用 guard + throws 替代隐式错误处理:guard 语句在函数入口拦截非法输入,throws 让错误传播路径在类型签名中可见。调用方必须用 try 标记,编译器保证你不会遗漏错误处理。怎么开始:把直接修改数组/字典的代码改成 mutating 方法,加入 guard 校验和 throws 标记。

  3. 用 actor 替代手动加锁:共享可变状态在并发环境下容易产生数据竞争。actor 自动序列化访问,编译器检查 Sendable 约束,比手动加锁更可靠。怎么开始:找到用 class + lock 管理共享状态的代码,替换为 actor,让编译器帮你验证并发安全。

关联 Session

评论

GitHub Issues · utterances