一、 Swift 语言中的实现方式
Swift 没有像传统 C 语言那样真正的“全局变量对象”概念,但它通过全局常量/变量或单利模式(Singleton)来实现相同的效果。Swift 的全局变量是懒加载(Lazy)的,并且在多线程下是线程安全的。
1. 全局直接定义(最简单)
直接在任何文件的最外层(不属于任何 Class 或 Struct)定义一个对象。这个对象可以拥有自己的属性。
import Foundation
// 定义一个拥有属性的类
class GlobalConfig {
var apiEndpoint: String = "https://api.example.com"
var timeoutInterval: TimeInterval = 30.0
}
// 方式 1:直接在文件最外层定义全局变量对象
let sharedConfig = GlobalConfig()
// 使用示例:
func checkConfig() {
// 直接在任何地方访问和修改属性
print(sharedConfig.apiEndpoint)
sharedConfig.timeoutInterval = 15.0
}
2. 结构体静态属性(Struct Static Property)
这是 Swift 中最推荐的方式,利用 static 关键字将变量绑定在命名空间(Struct 或 Class)下,避免全局命名空间污染。
struct AppTheme {
// 静态属性:作为全局变量对象
static var current = AppTheme()
// 对象的具体属性
var primaryColor: String = "#FFFFFF"
var fontSize: Double = 16.0
}
// 使用示例:
func applyTheme() {
// 通过 类型名.静态变量.属性 访问
print(AppTheme.current.primaryColor)
AppTheme.current.fontSize = 18.0
}
3. 标准单例模式(Singleton)
如果你希望这个全局对象只能有一个实例,不允许别人在其他地方单独 init 创建新对象,使用单例模式最严谨。
class AppManager {
// 1. 唯一的全局访问点
static let shared = AppManager()
// 2. 全局变量对象的属性
var isUserLoggedIn: Bool = false
var userToken: String?
// 3. 私有化初始化方法,防止外部再次 new 或 init
private init() {}
}
// 使用示例:
func loginSuccess() {
AppManager.shared.isUserLoggedIn = true
AppManager.shared.userToken = "abc123xyz"
}
复习知识点 Class & Struct
1. 核心区别一:值类型 vs 引用类型(最本质的区别)
这是 struct 和 class 最核心的差异,直接影响了全局变量的安全。
- Struct 静态属性(值类型): 当你访问
AppTheme.current并把它赋值给一个新变量时,Swift 会复制一份全新的副本。你在新变量上修改属性,不会影响全局的那个AppTheme.current。 - Class 标准单例(引用类型): 无论你把
AppManager.shared赋值给多少个变量,它们指向的都是同一个内存地址(堆内存) 。任何人在任何地方修改了属性,所有人看到的数据都会同步改变。
2. 核心区别二:唯一性的绝对限制(防呆机制)
- Struct 静态属性: 虽然它有一个静态属性
static var current,但它无法阻止别人在项目的其他地方通过let anotherTheme = AppTheme()随意创建成百上千个新的主体对象。 - Class 标准单例: 单例模式有一个关键动作——
private init()(私有化初始化方法) 。这意味着,除了它自己,项目里任何其他地方写let newManager = AppManager()都会直接报错无法编译。它在语法层面上强制保证了整个 App 运行期间有且仅有一个实例。
3. 核心区别三:多线程安全与数据竞争(Data Race)
- Class 单例(更需注意线程安全): 因为所有人共享同一个实例,如果线程 A 正在写入
userToken,线程 B 同时在读取userToken,就会发生数据竞争(Data Race)导致程序崩溃。在多线程高并发环境下,单例内部通常需要加锁(如使用DispatchQueue或 Swift 新特性的actor)来保证安全。 - Struct 静态属性(天然避免部分多线程问题): 由于结构体是值类型,在传递时会被复制,每个线程如果操作的是自己的副本,就天然避免了共享内存的冲突。但如果你直接并发去修改
AppTheme.current这个全局静态变量本身,依然需要注意安全。
| 特性 / 需求 | Struct 静态属性 方案 | Class 标准单例 方案 |
|---|---|---|
| 底层类型 | 值类型 (Value Type) | 引用类型 (Reference Type) |
| 外部能否再次 new 新对象 | 可以,无法限制 | 绝对不行 (被 private init 封死) |
| 赋值给新变量时的行为 | 复制一份新数据 (Copy) | 传递指针,指向同一个内存 (Share) |
| 最适合的场景 | 只读的全局配置、UI主题颜色、字体大小等只读数据集。 | 状态管理、网络请求封装(NetworkManager)、数据库管理(DatabaseManager)等需要全局同步状态的管理器。 |