Swift 访问控制(Access Control)详解
Swift 的访问控制用于限制代码中类型、属性、方法、初始化器、下标的成员的访问权限,避免外部误用内部实现,提高封装性。
1. 访问级别概览(由高到低)
| 级别 |
关键字 |
说明 |
open
|
open
|
类和类成员可被继承和重写(仅类可用) |
public
|
public
|
可被任何模块访问,可重写(但类不可继承) |
internal
|
internal
|
(默认)仅当前模块内可访问 |
fileprivate
|
fileprivate
|
仅当前源文件内可访问 |
private
|
private
|
仅当前作用域(类、结构体、枚举、扩展)内可访问 |
模块(Module):一个独立编译单元(如 Framework、App Target)
源文件(Source File):一个
.swift
文件
2. 各访问级别详解
open
(最高)仅用于类和类成员允许其他模块继承和重写
open class Vehicle {
open func start() { print("Engine started") }
}
// 另一个模块
class Car: Vehicle {
override open func start() { print("Car started") } // 允许
}
public
可被任何模块访问类成员可被重写,但类本身不可被继承
public class Logger {
public func log(_ msg: String) { print(msg) }
}
// 外部模块
class MyLogger: Logger { } // 错误!public 类不可继承
想允许继承?用
open
internal
internal
(默认)模块内部任意访问不写关键字即为
internal
class DatabaseManager { // internal
func connect() { ... }
}
fileprivate
仅当前
.swift
文件内可访问常用于辅助方法、扩展
struct User {
fileprivate var id: Int = 0
}
extension User {
func reset() {
id = 0 // 同一文件,可访问
}
}
// 其他文件
let user = User()
user.id // 错误!fileprivate
private
仅当前作用域(类、结构体、枚举、扩展)内可访问最严格
class BankAccount {
private var balance: Double = 0
func deposit(_ amount: Double) {
balance += amount
}
}
extension BankAccount {
func info() -> String {
return "Balance: \(balance)" // 同一扩展,可访问
}
}
let account = BankAccount()
account.balance // 错误!
3. 访问控制适用范围
| 成员 |
可设置访问级别 |
| 类型(class/struct/enum) |
Yes |
| 属性(var) |
Yes |
| 方法(func) |
Yes |
| 初始化器(init) |
Yes |
| 下标(subscript) |
Yes |
| 协议(protocol) |
Yes |
| 扩展(extension) |
Yes(扩展本身无级别,继承被扩展类型的级别) |
4. 关键规则(必须遵守)
规则 1:成员不能比其类型更开放
public class MyClass {
private func secret() { } // 正确
public var name: String // 正确
}
public struct MyStruct {
fileprivate var id: Int // 错误!fileprivate < public
}
修正:
internal var id: Int
或降低
MyStruct
为
internal
规则 2:函数参数和返回值不能比函数更严格
class Helper { }
public class API {
private func process(data: Helper) { } // 错误!参数比函数更严格
}
修正:
internal func process(data: Helper)
规则 3:元组类型的访问级别 = 最严格成员的级别
func getInfo() -> (String, fileprivate Struct) {
// 返回类型为 fileprivate
}
规则 4:getter/setter 可单独设置
public struct User {
private(set) var id: Int = 0 // 可读,外部不可写
public var name: String { // 可读可写
get { _name }
}
set { _name = newValue }}
private var _name: String = ""}
let user = User()
print(user.id) // 正常
user.id = 1 // 不正确!
user.name = "Tom" // 正常
5. 扩展(Extension)中的访问管理
public class Counter {
private var value = 0
}
extension Counter {
func increment() { value += 1 } // 默认继承 Counter 的层级
fileprivate func debug() { print(value) }
}
扩展中增加的成员,访问层级可单独设定
6. 协议(Protocol)的访问管理
public protocol Loggable {
func log()
}
internal protocol Configurable { ... }
协议需求的所有成员,
至少
要与协议一样开放
public protocol JSONExportable {
func toJSON() -> String
}
struct Post: JSONExportable {
func toJSON() -> String { ... } // 至少 internal
}
7. 初始化器(init)的特别规定
默认规定:
struct
:无参数初始化器与结构体同级
class
:指定初始化器与类同级
自定义初始化器:
public struct Point {
public var x, y: Double
private init() { // 私有构造
x = 0; y = 0
}
public init(x: Double, y: Double) {
self.x = x; self.y = y
}
}
外部无法调用
Point()
,只能用
Point(x:y:)
8. 实际应用情境
情境
推荐访问层级
公开 API(如 SDK)
public
/
open
框架内部工具类
internal
辅助方法
fileprivate
敏感信息
private
只读属性
private(set)
9. 常见错误实例
// 错误 1:成员比类型更公开
public struct API {
fileprivate var token: String // 错误!
}
// 错误 2:参数比函数严格
public func login(user: private User) // 错误!
// 错误 3:协议要求未达成
public protocol Validatable {
func validate() -> Bool
}
struct Form: Validatable {
private func validate() -> Bool { true } // 错误!private < public
}
10. 最佳实践(Best Practices)
// 1. 默认采用 private / fileprivate
class NetworkManager {
private let session = URLSession.shared
private func parse<T: Decodable>(_ data: Data) -> T? { ... }
}
// 2. 公开 API 需明确标示 public
public struct APIClient {
public init() { }
public func request<T: Decodable>(_ type: T.Type) -> T? { ... }
}
// 3. 利用 private(set) 保护状态
public struct Counter {
public private(set) var count = 0
public mutating func increment() { count += 1 }
}
11. 访问控制 vs 封装
目标
方式
隐藏实现细节
private
/
fileprivate
阻止继承
final
+
private
控制读写权限
private(set)
模块隔离
internal
面试常见题
open
和
public
的区别?
→
open
允许继承和覆盖,
public
仅允许覆盖。
为什么
private
成员在扩展中仍可访问?
→ 扩展与原类型处于
相同的作用域
。
如何使结构体仅能通过工厂方法创建?
public struct User {
private init() { }
public static func create() -> User { User() }
}
总结:访问控制金字塔
open ← 仅类,允许继承重写
public ← 跨模块访问
internal ← 模块内(默认)
fileprivate ← 文件内
private ← 作用域内(最严)
需要
访问管理 + 组件化设计实践
?例如:
构建一个
CoreKit
结构
设计
public API
与内部细节隔离
利用
private
避免误操作
欢迎进一步提问!