从实例到单例:Objective-C 单例类的线程安全实现方案
在 Objective-C 中实现线程安全的单例模式需解决两个核心问题:
- 确保全局唯一实例
- 防止多线程环境下的竞态条件
一、基础实现方案
objectivec
// MySingleton.h
@interface MySingleton : NSObject
+ (instancetype)sharedInstance;
@end
// MySingleton.m
@implementation MySingleton
+ (instancetype)sharedInstance {
static MySingleton *_sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedInstance = [[self alloc] init];
});
return _sharedInstance;
}
@end
关键组件:
static变量保证实例生命周期与程序相同dispatch_once_t确保代码块仅执行一次- GCD 的
dispatch_once提供原子性操作
二、线程安全分析
设 T_1, T_2 为并发线程,其执行过程满足: $$P(T_1 \cap T_2) = \emptyset \implies \text{单例唯一性}$$ dispatch_once 的底层实现基于:
- 原子性内存屏障
- 信号量锁机制
- 双重检查锁定模式
三、防御性改进方案
防止通过其他方式创建实例:
objectivec
// 重写 allocWithZone 方法
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
return [self sharedInstance];
}
// 防止拷贝
- (id)copyWithZone:(NSZone *)zone {
return self;
}
四、性能对比
| 实现方案 | 线程安全 | 时间复杂度 | 内存屏障 |
|---|---|---|---|
@synchronized |
是 | O(n) | 有 |
dispatch_once |
是 | O(1) | 无 |
| 双重检查锁定 | 否 | O(1) | 无 |
最佳实践:
- 始终使用
dispatch_once方案- 在
.m文件中声明静态变量而非头文件- 重写
allocWithZone:和copyWithZone:方法- 避免在单例初始化方法中调用
sharedInstance
五、使用场景
适合以下全局服务:
- 配置管理器
- 网络监控器
- 核心数据栈
- 日志系统
graph LR
A[调用 sharedInstance] --> B{实例是否存在?}
B -- 否 --> C[dispatch_once 创建]
B -- 是 --> D[返回现存实例]
C --> D
此方案满足: $$ \forall t \in \mathbb{T}, \quad \exists! , \text{instance} \in \text{MySingleton} $$ 其中 \\mathbb{T} 表示线程集合,\\exists! 表示存在且唯一。