Swift 真的被搞得乱七八糟了吗?写了几年之后说点实话

Swift 真的被搞得乱七八糟了吗?写了几年之后说点实话

最近跟几个 iOS 同行聊天,不止一个人吐槽:"Swift 这几年加的东西太多了,把一个好好的语言搞乱了"。翻社区也能看到类似的声音------有人说 Swift 正在变成"下一个 C++",有人说苹果根本不懂语言设计,每年 WWDC 就是往语言里硬塞新玩具。

我自己写 Swift 也有些年头了,从 2.0 版本到现在 6.x,亲眼看着它从一个"现代版 Objective-C"变成今天这个样子。要说没感受到膨胀是假的,但要说"被搞烂了"我又不太同意。这事儿比表面看起来复杂。

今天就聊聊这个话题。

膨胀是真的,先认个账

先别急着为 Swift 辩护。把这些年加进来的东西列一下,你就知道批评不是没道理:

并发这一整套 :async/await、actor、Sendable、@MainActor、structured concurrency、isolated 参数、nonisolated(unsafe)、region-based isolation、Swift 6 的完整 data race safety......

所有权模型borrowingconsuming~Copyable~Escapable、move-only types。

宏系统@freestanding@attached,能在编译期生成代码。

Result builder:SwiftUI 那套 DSL 的底座。

Property wrapper@State@Published@Observable@Bindable......

泛型的演进 :opaque types(some)、existential types(any)、primary associated types、parameter packs、~Copyable 泛型约束。

各种 attribute@MainActor@preconcurrency@retroactive@unchecked Sendable......

写一个稍微复杂点的函数签名,可能长这样:

swift 复制代码
func process<T>(_ items: some Collection<T>) -> any AsyncSequence<T, Error>
    where T: Sendable & ~Copyable

每个关键词都有道理,但堆在一起确实让人喘不过气。尤其是从 Swift 3、4 那个时代过来的人,会明显感觉"这不是我认识的 Swift"。

这就是批评的源头。膨胀是客观事实,不用洗。

但为什么会变成这样?三个真实原因

骂完之后,我们得冷静想想:苹果为什么要这么做? 是他们真的不懂设计,还是有什么东西逼着他们必须加?

原因一:Swift 想同时当几种语言

这是最根本的问题。Swift 的野心比 Objective-C 大太多:

  • 应用层语言(替代 OC,写 iOS/macOS app)
  • 系统级语言(替代 C/C++,能写内核、嵌入式、驱动)
  • 服务端语言(Vapor 那套)
  • DSL 宿主(SwiftUI、Regex Builder、SwiftData)

一个语言要同时满足这四种场景,天然会膨胀。这不是 Swift 独有的命,C++ 是这样,Rust 正在走这条路。你想要"安全 + 高性能 + 表达力",只能不断加特性。

反例是 Go。Go 刻意不做这些------不要泛型(后来才加)、不要宏、不要所有权系统。结果是语言简洁了,但写应用层代码啰嗦、写 DSL 几乎不可能。简洁是用表达力换的,不是白来的。

苹果选了另一条路:我什么都要。代价就是你今天看到的 Swift。

原因二:并发和所有权的复杂度,是物理级的

很多吐槽 Swift 的人,把 Sendable@MainActorborrowing 这些东西当成"苹果瞎加的"。其实不是------这两块复杂度是本质的

并发安全 :你想在编译期证明一段代码没有数据竞争,就必须引入 Sendable、actor、isolation domain 这些概念。Rust 靠 Send/Sync trait,Swift 靠 Sendable/@MainActor,两边复杂度其实差不多。

所有权 :你想消除 ARC 开销、支持 move semantics,就必须有 borrowing/consuming。Rust 这块甚至更复杂(还有 lifetime 标注)。

这是计算机科学的硬约束。 你不能既要"零成本抽象 + 内存安全 + 并发安全"又要"语法简单"。三者不可兼得。Swift 选了前者,就得付出后者的代价。

骂 Swift 这部分复杂,本质上是在骂并发编程本身复杂、系统编程本身复杂------那是真的复杂,不是苹果的锅。

原因三:向后兼容的累积

Swift 为了不把老项目全部搞挂,新特性往往是叠加 而不是替换

  • any Psome P 并存(existential vs opaque)
  • async throws 和老的 completion handler 并存
  • @Observable 宏和老的 ObservableObject 协议并存
  • 新的 typed throws 和老的 throws any Error 并存

所以你学 Swift 现在要学两套做同一件事的方法,一套给老代码,一套给新代码。

这是所有长寿语言的通病。看看 C++ 的 auto_ptrunique_ptr、Java 的 DateCalendarjava.time、Python 的 % 格式化 → .format() → f-string。能用十年的语言,没一个不膨胀的。 因为你不能扔掉已经写好的几十亿行代码。

哪些批评我是真的同意

但是,别以为我是全盘为 Swift 辩护。有些膨胀确实是设计问题,不是物理约束:

并发关键词太多,语义重叠

@MainActor@preconcurrency@Sendablenonisolatedisolated@unchecked Sendable------同一件事(并发隔离)用了太多关键词,很多组合语义要反复查文档才能确认。

这个完全可以设计得更简洁。Swift 团队在这块明显是分多次迭代上的,每次加一点,最后拼起来就显得零碎。

泛型语法越来越读不懂

还是上面那个例子:

swift 复制代码
func process<T>(_ items: some Collection<T>) -> any AsyncSequence<T, Error>
    where T: Sendable & ~Copyable

每个部分分开都能解释清楚,但组合起来的认知负担极大。Swift 在"表达力"和"可读性"之间,明显偏向了前者。这是设计选择,不是必然的。

对比 Kotlin、Go,它们的泛型同样够用,但没搞到这种程度。

SwiftUI 的魔法太多

@State@Binding@Environment@Observable@Bindable------property wrapper 解决的问题是真的(双向绑定、依赖追踪、环境传递),但新手要理解一个 SwiftUI view 为什么会自动刷新,得先懂一整套元编程

这跟 React hooks 的问题很像:隐藏了太多控制流,写起来爽,debug 起来哭。SwiftUI 一个预览不工作,你可能要排查十几个可能原因。

编译器错误信息依然很烂

这是 Swift 被吐槽最久的点,十年了没有根本改善。泛型或 SwiftUI 出错时,编译器报错经常几十行,指向一个根本不相关的地方。经典的 error: type of expression is ambiguous without more context,写 Swift 的人看到这句话估计都有 PTSD。

更公允的说法

综合上面的分析,我觉得对 Swift 的评价应该是这样:

Swift 的膨胀 = 必然代价 + 部分设计失误

必然代价那部分(并发、所有权、泛型演进),骂它"搞乱"其实是骂计算机科学本身。选哪条路都有代价,Rust 更复杂,C++ 更混乱,Kotlin 为了简单牺牲了一些能力。

设计失误那部分(关键词过多、语法噪音、错误信息),骂得对。Swift 团队在功能添加 上很激进,在清理和简化上很保守------一年一个 WWDC 逼着不断出新,但很少见他们下决心废掉一个老语法。

还有一个更重要的观察:

Swift 不是一个语言,是一个语言的多个层级。

  • 日常 app 业务代码,其实依然简洁、现代、愉快(这是 90% 人的使用场景)
  • 库代码、框架代码、性能敏感代码,才会遇到所有膨胀的复杂度(这是 10% 人的场景)
  • 吐槽声最大的人,很多是看到了所有特性的文档和 WWDC session,但实际写代码 80% 特性用不到

这跟 C++ 特别像:C++ 的复杂度是真的,但写应用层 C++ 不需要懂模板元编程。Swift 也一样------你用到哪层就承担哪层的复杂度。

真正的问题是 Swift 对"多层级 "这件事说得不够清楚,没有明确告诉开发者"你写 app 可以忽略 A/B/C,只有写库才需要 D/E/F"。WWDC 一股脑把新特性全扔出来,文档也不分层次。结果就是所有人看到满屏 @~ 就觉得这语言疯了。

所以到底该不该学 Swift?

经常有人私信问我这个问题,尤其是想入 iOS 的新人。我的回答一直是:

该学,但别被吓到。

  • 如果你只是写 app,Swift 依然是现代化、好用的语言。SwiftUI 上手成本比 UIKit 低。那些 @unchecked Sendable 之类的玩意儿,你基本用不到。
  • 如果你想深入做框架、做库、做性能优化,那你就得逐步攻克那些复杂特性------但这是任何系统级语言都需要的功夫,不是 Swift 独有的。
  • 真正的难点其实不是语法,是理解苹果生态的一堆约定:runloop、main thread、Combine、Concurrency、SwiftUI 的生命周期......这些比语言本身更花时间。

至于学习方法,我给一个实在的建议:不要一口气把所有特性文档看完。没用,看完就忘。

正确的路径是:遇到问题再查,查的时候顺便问清楚来龙去脉 。比如你写 SwiftUI 遇到 @Observable 报错,就借机把 property wrapper 和 Observation 框架的演进搞清楚;你写 async 代码遇到 data race 警告,就借机理解 Sendable 和 actor isolation。

这种"问题驱动"的学习最扎实。

写到最后

吐槽归吐槽,Swift 依然是我这几年写得最多、也最顺手的语言。它有毛病,但没被搞坏。

苹果团队面对的约束其实比旁观者想象的多得多------既要保留旧代码兼容,又要追赶 Rust 在系统编程上的优势,还要服务 SwiftUI 那种 DSL 的表达需求,最后还要对付亿万普通开发者。做成现在这样,说实话已经不容易了。

对一门工业语言最公平的评价,永远不是拿它和教科书里的"理想语言"比,而是拿它和它的同代人比。Swift 比同时期的 Kotlin 表达力强,比 Rust 好学,比 Go 功能全,比 C++ 安全。它的膨胀是这些优势的代价。


顺便说一下,写这篇文章的过程中我又把几个 Swift 新特性的 proposal 翻了一遍(swift-evolution 那个仓库),很多设计背后的讨论其实挺有意思。如果你对某个特性"为什么要这样设计"好奇,强烈推荐去翻原始 proposal,比 WWDC 的宣传片信息量大得多。

平时我遇到这类"某个 API 为什么要这么设计"、"这个新特性值不值得用"的问题,习惯打开 www.gufacode.com 顺手聊两句再决定。不是让它给标准答案,是当个陪聊对象把思路过一遍------有时候问题本身想清楚了,答案也就自己出来了。

就这样,有不同意见欢迎评论区拍砖。

相关推荐
唐诺2 小时前
iOS UI 框架详解
ui·ios
Zender Han2 小时前
Flutter 轻量存储方案介绍、区别、对比和使用场景
android·flutter·ios
2501_916007472 小时前
XCode 15 IDE新特性:苹果集成开发环境全面升级,提升编程效率与体验
ide·vscode·macos·ios·个人开发·xcode·敏捷流程
东坡肘子2 小时前
CocoaPods 正在退场,SwiftPM 才刚到第二章 -- 肘子的 Swift 周报 #135
flutter·swiftui·swift
MonkeyKing71552 小时前
iOS Tagged Pointer 原理、判断方式、适用场景与避坑指南
ios·objective-c
飞Link16 小时前
iOS 27 开启“AI 开放时代”:Siri 驱动可更换背后的技术范式迁移
人工智能·ios
泉木18 小时前
KVC 详解 —— Key-Value Coding 完全指南
ios·swift
sweet丶19 小时前
现有基础上增加设备生物识别登录的一个技术方案
ios