我们称 Alamofire 为库,称 SwiftUI 为框架。但它们不都是“拿来使用别人编写的代码”吗?到底有什么不同?
人们常听到的答案是“框架更大”,但大小并不是本质。既有小型框架,也有庞大的库。
谁调用谁。
抓住这一个标准,就能自然联系到控制反转(IoC)这一重要概念。学习依赖注入时还会再次遇到它,所以在这里打好基础很有帮助。
下面是核心总结。
- 库:我的代码在需要时调用的工具集合。控制权在我手中
- 框架:掌控流程并调用我的代码的骨架。控制权在框架手中
- 这种反转的调用方向称为控制反转
- 好莱坞原则:“别调用我们,我们会调用你”
库:由我调用的工具
库是一组预先实现特定功能的代码。何时使用、按什么顺序使用,完全由我的代码决定。
// 流程由我的代码掌控
let json = try JSONDecoder().decode(User.self, from: data)
let hash = SHA256.hash(data: input)
程序的开始、结束和整体流程由我设计,然后在过程中取用所需的工具。这就像从工具箱里拿出螺丝刀。螺丝刀不会决定工作的顺序。
Alamofire、Kingfisher 都属于这一类。何时发送网络请求、何时加载图片,由我决定。
框架:调用我的骨架
框架是预先构建好应用整体结构和执行流程的骨架。我只需在它预留的位置填入代码。
以 iOS 应用为例就很清楚了。应用的入口、事件循环和界面生命周期都由 UIKit、SwiftUI 掌控。我编写的 viewDidLoad、body、onAppear 等,是框架在指定时机调用的代码。
struct ProfileView: View {
var body: some View { // 不是由我调用.
Text("Hello") // SwiftUI在需要时调用
}
}
我不会手动调用 body。何时调用、调用多少次由 SwiftUI 决定。流程的主导权已经换手。
控制反转与好莱坞原则
这种反转的关系有一个名字:控制反转(IoC)。
在普通的过程式程序中,我的代码掌控控制流并调用外部代码。在基于框架的程序中,框架掌控流程,而我的代码会在注册的位置被调用。控制权转向了相反方向,所以称为“反转”。
对它的巧妙表达就是好莱坞原则。
“别调用我们,我们会调用你。”——别打电话来,我们会联系你。
参加试镜的演员不会不断给制作公司打电话,而是在被选中后由制作公司联系。框架与我的代码之间正是这种关系。
委托模式也是同样的原理。实现 UITableViewDataSource 时,是我调用 cellForRowAt 吗?不是。表格视图需要时会调用我。框架世界的语法已经渗透到代码的各个角落。
所以在实际工作中有什么不同?
学习方式不同。使用库时,只需查找“有哪些功能”;使用框架时,则要先了解“它什么时候调用我”(生命周期和调用约定)。这就是学习 UIKit 要从生命周期开始的原因。
替换成本不同。库只需修改调用位置就能替换,但整个代码库都建立在框架骨架之上,因此替换实际上等同于重写。这就是从 UIKit 切换到 SwiftUI 不只是修改几个函数的原因。
测试策略不同。框架调用的代码很难脱离框架单独运行。因此,人们才会反复建议把逻辑拆分到不依赖框架的层(纯 Swift)中。
面试时用一句话说明
“库是由我的代码调用的工具,而框架是掌控控制流并调用我的代码的骨架。这种控制权方向的差异称为控制反转。”
后续问题通常是“请举一个 IoC 的例子”(生命周期方法、委托),然后是“这样有什么好处?”(标准化流程,让开发者专注于业务逻辑)。
总结
- 区分标准不是大小,而是调用方向
- 库:我的代码在需要时调用的工具。控制权在我手中
- 框架:掌控流程并在指定时机调用我的代码的骨架。控制权在框架手中
- 这种反转的控制权就是控制反转(IoC),别名是好莱坞原则
- viewDidLoad、body 和委托方法全都是“被调用”的代码
- 实际差异:先学习框架生命周期,替换困难,而分离逻辑是测试的关键

