在 Swift 中,访问控制(Access Control)遵循一个核心原则:实体只能在它被定义的上下文中访问,除非你显式提升了它的权限。
这五个关键字构成了 Swift 的权限金字塔,从最严格到最宽松排列如下:
1. private(最严格)
作用范围: 仅限在**当前定义的作用域({})**以及同文件内的扩展(extension)中访问。
-
适用场景:
- 隐藏类的内部实现细节(如私有属性、辅助方法)。
- 防止外部直接修改状态,确保封装性。
- 优化编译性能(编译器可以确定该属性不会被外部修改,从而进行内联优化)。
2. fileprivate
作用范围: 仅限在**当前源文件(.swift 文件)**内访问。
-
适用场景:
- 当一个文件内有多个类、结构体需要共享某些属性,但不希望文件外部看到时。
- 在同一个文件中定义复杂的逻辑拆分,但仍保持逻辑闭环。
3. internal(默认级别)
作用范围: 在**整个模块(Module / Target)**内均可访问,但模块外部不可见。
-
适用场景:
- App 项目内部的大部分代码。
- 在同一个 Swift Package 或 Framework 内,不同类之间的协作。
- 注意: 如果不写任何关键字,Swift 默认就是
internal。
4. public
作用范围: 可以在模块外部访问(即被其他 App 或 Library import 后使用)。
-
适用场景:
- 定义框架(Framework)对外的 API。
- 允许外部调用,但禁止外部在其定义的模块外进行继承(Subclass)或重写(Override)。
-
限制: 保证了库的稳定性,防止用户通过继承破坏库的内部逻辑。
5. open(最宽松)
作用范围: 与 public 相同,但允许在模块外部进行继承和重写。
-
适用场景:
- 设计需要被用户高度定制的框架类(如
UIKit的UIViewController)。 - 仅适用于类(Class)和类成员,不适用于结构体和枚举。
- 设计需要被用户高度定制的框架类(如
访问权限对比与关系
为了更直观地理解,可以参考以下表格:
| 级别 | 跨文件访问 (同模块) | 跨模块访问 (import) | 跨模块继承/重写 | 默认级别 |
|---|---|---|---|---|
| private | ❌ (仅限作用域) | ❌ | ❌ | |
| fileprivate | ✅ (仅限同文件) | ❌ | ❌ | |
| internal | ✅ | ❌ | ❌ | ✅ |
| public | ✅ | ✅ | ❌ | |
| open | ✅ | ✅ | ✅ |
核心建议:如何选择?
-
坚持“最小特权原则” :默认先使用
private,当发现无法访问时再逐步放开。这不仅能保护数据安全,还能让编译器更好地优化代码。 -
区分“调用者”与“继承者” :
- 如果你希望别人只能用你的代码,用
public。 - 如果你希望别人能扩展你的功能,用
open。
- 如果你希望别人只能用你的代码,用
-
针对多模块项目:
- 即使是同一项目,拆分成不同 Package 后,只有标记为
public或open的部分才能跨模块通信。
- 即使是同一项目,拆分成不同 Package 后,只有标记为
-
属性 Setter 的特殊处理:
- 你可以使用
private(set) public var count: Int。这意味着外面的人能读取count,但只有内部能修改它。这在状态管理中非常实用。
- 你可以使用