Swift 访问控制有哪些级别?open 和 public 有什么区别?
Swift 有五个访问级别,从宽松到严格:open > public > internal > fileprivate > private。internal 是默认值——不写访问修饰符就是 internal。
核心区别在于两个维度:谁能访问(同模块/跨模块/同文件/同作用域),和能不能继承重写(只有 open 允许跨模块继承和重写)。
| 级别 | 同模块 | 跨模块 | 可继承重写 |
|---|---|---|---|
| open | ✅ | ✅ | ✅(跨模块) |
| public | ✅ | ✅ | ❌(跨模块不可重写) |
| internal | ✅ | ❌ | ✅(同模块内) |
| fileprivate | 同文件 | ❌ | 同文件内 |
| private | 同作用域 | ❌ | 同作用域内 |
open 和 public 的唯一区别:open 允许跨模块继承和重写,public 不允许。框架对外暴露的基类用 open,工具类/辅助类用 public。
追问
open 和 public 有什么区别?什么时候用 open?
open 允许其他模块继承类和重写方法,public 只允许访问不允许继承重写。用 open 的场景:你设计一个框架,希望使用者能继承你的基类来定制行为——UIKit 的 UIViewController 就是 open 的。用 public 的场景:功能完整的类,不希望被子类化——比如工具类、配置类。
fileprivate 和 private 有什么区别?
private 限制在定义的作用域内——类里的 private 属性,类的方法能访问,但扩展(同文件)不能访问(Swift 4 之前)。fileprivate 限制在定义的文件内——同文件的所有类型和扩展都能访问。Swift 4 之后 private 在同文件的扩展里也能访问了,所以 fileprivate 的用武之地变少了。如果多个类型需要共享某个属性或方法,放在同文件里用 fileprivate。
默认访问级别是什么?为什么不写修饰符就是 internal?
Swift 的哲学是"模块即边界"——大部分代码只在模块内部使用,不需要暴露给外部。internal 正好对应这个边界:模块内可见,模块外不可见。不写修饰符就是 internal,减少了大量样板代码。只有明确需要跨模块的 API 才需要写 public 或 open。
子类的访问级别可以比父类更严格吗?
可以。子类可以把父类的 open 方法重写为 public,但不能反过来——父类是 public 的方法,子类不能重写为 open。规则是"子类的访问级别不能比父类更宽松",但重写时可以收紧。属性也一样——父类 public var,子类可以重写为 internal var。
访问控制和泛型有什么交互?
泛型类型的访问级别取决于类型本身和泛型参数中最严格的那个。public class Container<T: PrivateProtocol> 编译不过——public 类型不能依赖 private 协议。函数同理:public func process(_ item: InternalType) 也编译不过。规则是"实体的访问级别不能比它依赖的类型更高"。
写段代码
swift// 框架对外 API open class BaseService { open func execute() { } // 允许跨模块重写 public func validate() { } // 允许跨模块调用,但不允许重写 } // 模块内部实现 class InternalHelper { // 默认 internal fileprivate func assist() { } // 同文件可访问 private var state: Int = 0 // 同作用域可访问 } // 子类收紧访问级别 class MyService: BaseService { public override func execute() { } // ✅ 收紧:open → public }