学习 OOP 时,“封装”这个词大概会听上百遍,但真被问到“那代码里到底怎么做?”时,往往会突然语塞。概念懂了,手却不知道怎么写。
先说结论,封装就是“隐藏对象内部数据,防止外部随意修改,只允许通过规定的路径访问”。在 Swift 中,它通过访问修饰符实现。
今天我们会用实际代码逐一查看 private、internal 等 5 个访问控制级别,并整理信息隐藏为何能保护代码。
封装到底是什么?
封装(Encapsulation)是 OOP 四大特征之一。
用一句话概括:
将数据与处理数据的功能绑定在一起,隐藏内部实现,只向外部开放必要部分。
为什么要隐藏?
想想银行账户就容易理解了。如果任何人都能直接把余额改成任意数字,后果会很严重。
存款和取款必须经过规定流程,并确保“不能取出超过余额的金额”等规则得到遵守。
像这样阻止直接操作内部值,只开放经过验证的路径,就叫作信息隐藏。
如果说封装是更大的概念,那么信息隐藏就是实现它的核心原则。
Swift 五种访问控制级别对比一览
Swift 将访问范围分为 5 个级别,下面按范围从窄到宽整理。
(以 2026 年 Swift 最新语法为准。)
| 访问修饰符 | 可访问范围 | 主要用途 |
|---|---|---|
private |
声明所在的大括号块内部 | 真正想隐藏的内部状态 |
fileprivate |
整个相同源文件 | 同一文件中的协作类型 |
internal |
整个相同模块(应用/框架) | 默认值,大多数代码 |
public |
也可从其他模块使用 | 库对外公开的 API |
open |
允许从其他模块继承和重写 | 框架扩展点 |
这里有一点一定要记住。
什么都不加时,internal就是默认值。
所以平时即使不写访问修饰符,在同一个应用内也能正常运行。
public和open的区别也很容易混淆。在其他模块中,只有open可以进行继承和重写。public虽然可以使用,但禁止继承。
用 private 实现真正的信息隐藏
光说不容易理解,我们直接看账户示例的代码。
先从没有封装的错误示例开始。
class BadAccount {
var balance: Int = 0 // 外部任何人都可以修改
}
let acc = BadAccount()
acc.balance = -99999 // 不合理的值也会直接写入
print(acc.balance)
// 输出: -99999
余额竟然是负 9 万,规则彻底崩溃了。
现在用private隐藏内部状态,只开放存款和取款路径。
class Account {
private var balance: Int = 0 // 阻止外部访问
func deposit(_ amount: Int) {
guard amount > 0 else { return }
balance += amount
}
func withdraw(_ amount: Int) -> Bool {
guard amount > 0, balance >= amount else { return false }
balance -= amount
return true
}
var currentBalance: Int { balance } // 只允许读取
}
let myAccount = Account()
myAccount.deposit(10000)
print(myAccount.withdraw(30000)) // 余额不足
// 输出: false
现在外部无法直接修改balance。
像myAccount.balance = -99999这样的代码会产生编译错误。
资金只能通过deposit和withdraw流动,验证规则也明确写在其中。
这就是信息隐藏的力量:从一开始就阻止你写出违反规则的代码。
private(set) — 开放读取,只禁止写入
接下来看看更实际的情况。
“允许外部读取值,但只禁止修改”是非常常见的需求。
像上面那样单独创建计算属性当然可以,但 Swift 提供了更简洁的方法。
class ScoreBoard {
private(set) var score: Int = 0 // 读取 public, 写入 private
func addPoint() {
score += 10
}
}
let board = ScoreBoard()
board.addPoint()
print(board.score) // 读取不受限制
// 输出: 10
// board.score = 999 // 这一行会产生编译错误
加上private(set)后,读取仍然开放,但写入权限会限制在内部。
由于分数只能通过addPoint()增加,因此可以防止游戏逻辑外部篡改分数。
我经常使用这种语法,因为不需要额外创建计算属性。
什么时候该使用哪个修饰符?
整理了几条在实际工作中感到困惑时可以参考的标准。
- **先从
private**开始。需要时再扩大范围,比一开始开放、之后再收窄更安全。 - **默认值
internal**在创建单个应用时大多都适用,不必全部显式写出。 - **
public·open**只有在创建供他人使用的库或框架时才需要考虑。 - 只有需要开放继承时才使用**
open。**否则public就足够了。
| 情况 | 判断 |
|---|---|
| 只在内部使用的状态变量 | private |
| 只想让外部读取 | private(set) |
| 在同一应用内自由使用 | internal(默认值) |
| 作为库对外公开 | public |
| 允许外部继承和重写 | open |
只需记住一点:访问范围始终应尽可能小地开放。
面试中会这样问
Q. 封装和信息隐藏有什么区别?
封装是将数据和方法绑定到同一个对象中的设计概念,而信息隐藏是将内部实现隐藏起来的原则。简洁地说,封装是更大的容器,信息隐藏则通过访问修饰符在其中实现。
Q. Swift 中 public 和 open 有什么区别?
两者都可以从其他模块访问,但只有open允许外部模块继承和重写方法。public类可以在外部使用,但不能被继承。补充说明只有在开放框架扩展点时才使用open即可。
访问修饰符看起来像是记住几个语法规则就够了,但实际上是在训练你设计“谁可以修改这个值”。
亲自敲一遍今天的账户示例,马上就能掌握。推荐在小型项目中先从private开始添加!

