Swift 访问控制

Swift 访问控制(Access Control)详解

Swift 的访问控制用于限制代码中类型、属性、方法、初始化器、下标等成员的访问权限,防止外部误用内部实现,增强封装性。


1. 访问级别概览(由高到低)

级别关键字说明
openopen类和类成员可被继承和重写(仅类可用)
publicpublic可被任何模块访问,可重写(但类不可继承)
internalinternal(默认)当前模块内可访问
fileprivatefileprivate当前源文件内可访问
privateprivate当前作用域(类、结构体、枚举、扩展)内可访问

模块(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
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() { } // OK
    public var name: String   // OK
}

public struct MyStruct {
    fileprivate var id: Int   // 错误!fileprivate < public
}

修正:internal var id: Int 或降低 MyStructinternal


规则 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)    // OK
user.id = 1       // 错误!
user.name = "Tom" // OK

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

面试高频题

  1. openpublic 的区别?
    open 允许继承和重写,public 只允许重写。
  2. 为什么 private 成员在扩展中仍可访问?
    → 扩展与原类型在同一作用域
  3. 如何让结构体只能通过工厂方法创建?
   public struct User {
       private init() { }
       public static func create() -> User { User() }
   }

小结:访问控制金字塔

open      ← 仅类,允许继承重写
public    ← 跨模块访问
internal  ← 模块内(默认)
fileprivate ← 文件内
private   ← 作用域内(最严)

需要 访问控制 + 模块化设计实战?如:

  • 构建一个 CoreKit 框架
  • 设计 public API 与内部实现分离
  • 使用 private 防止误用

欢迎继续提问!

文章已创建 2588

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部