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,要么是nilif 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
核心启发
-
用值语义建模数据:定义新类型时默认用 struct。值类型的赋值自动拷贝,天然隔离状态,在并发场景下更安全。只有需要引用语义(共享可变状态、身份、继承)时才用 class 或 actor。怎么开始:审查现有代码中的 class,如果没有共享状态需求,改成 struct。
-
用 guard + throws 替代隐式错误处理:guard 语句在函数入口拦截非法输入,throws 让错误传播路径在类型签名中可见。调用方必须用 try 标记,编译器保证你不会遗漏错误处理。怎么开始:把直接修改数组/字典的代码改成 mutating 方法,加入 guard 校验和 throws 标记。
-
用 actor 替代手动加锁:共享可变状态在并发环境下容易产生数据竞争。actor 自动序列化访问,编译器检查 Sendable 约束,比手动加锁更可靠。怎么开始:找到用 class + lock 管理共享状态的代码,替换为 actor,让编译器帮你验证并发安全。
关联 Session
- Embrace Swift generics — 深入讲解泛型约束、协议设计,是本场协议与泛型部分的自然延伸
- Explore structured concurrency in Swift — 系统讲解 Task、async/await、actor 的并发模型
- Demystify explicitly built modules — Xcode 16 构建系统变化,涉及 Swift 模块编译机制
- Expand on Swift macros — 宏的编译期代码生成能力,是属性包装器和 result builder 之外的另一种语言扩展手段
评论
GitHub Issues · utterances