前言 : 单例你我他,使用靠大家,其实的写法是不难的,重点就在代码的使用规范上,开发者在遵守规范编码的时候,能避开一些本不该出现的问题,但是每个开发者的思维总是不尽相同,所以在公司规范强制性稍微差点的项目中,个人风格一定会存在于代码中的。
简单的分析下单例创建的方式以及如何避开使用上的陷阱
单线程单例
+ (instancetype)sharedInstance{
static DOVESingleThreadSingleton *sharedInstance = nil;
if (!sharedInstance) {
sharedInstance = [[DOVESingleThreadSingleton alloc]init];
}
return sharedInstance;
}
- 单线程单例只有在单个线程使用的情况下才是安全的,在多个线程同时使用时,每个线程中都创建了一个非共享的实例,不具有安全性,
- 在多条线程中同时调用[DOVESingleThreadSingleton sharedInstance];方法,在没有加锁时,会产生多个singleton非共享实例,这违背了单例的原则
多线程加锁单例
通常使用@synchronized 或 dispatch_once的方式
@synchronized采用的是递归互斥锁来实现线程安全,而dispatch_once的内部则使用了很多原子操作来替代锁,以及通过信号量来实现线程同步,而且有很多针对处理器优化的地方,甚至在if判断语句上也做了优化(逼格有点高),使得其效率有很大的提升,虽然其源码很短,但里面包含的东西却很多,所以苹果也推荐使用dispatch_once来创建单例
1、使用 @synchronized 加锁
+ (instancetype)sharedInstance {
static DOVEMultithreadingsingeton *sharedInstance;
@synchronized (self) {
if (!sharedInstance) {
sharedInstance = [[DOVEMultithreadingsingeton alloc] init];
}
}
return sharedInstance;
}
- 因为使用@synchronized加了锁,所以避免了创建多个非共享实例。但是和dispatch_once加锁相比,性能差点
2、使用dispatch_once加锁
2.1、禁用外部调用除sharedInstance之外的方法
.h
-(instancetype)init NS_UNAVAILABLE;
+(instancetype)new NS_UNAVAILABLE;
-(id)copy NS_UNAVAILABLE;
-(id)mutableCopy NS_UNAVAILABLE;
+ (instancetype)sharedInstance;
.m
+(instancetype)sharedInstance{
static DOVEMultithreadingsingeton *sharedInstance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc]init];
});
return sharedInstance;
}
- 为了防止init、new等在不同线程中创建引起的错误,使用NS_UNAVAILABLE禁用相关的创建方法
2.2 、不禁用其他方法,但要做保证安全的处理
+(instancetype)sharedInstance{
static DOVEMultithreadingsingeton *sharedInstance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[super allocWithZone:NULL] init];
});
return sharedInstance;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone{
return [DOVEMultithreadingsingeton sharedInstance];
}
-(id)copy{
return [DOVEMultithreadingsingeton sharedInstance];
}
-(id)mutableCopy{
return [DOVEMultithreadingsingeton sharedInstance];
}
- (id)mutableCopyWithZone:(nullable NSZone *)zone {
return [DOVEMultithreadingsingeton sharedInstance];
}
- 对各种初始创建的方法做安全处理
- 因为重写了初始化创建的方法,所以不能在使用[[self alloc]init], 该用调用父类的分配空间,如果使用了,那么会与[[allocWithZone:NULL]init]造成死锁
使用单例在整个项目中只会创建一次共享实例,故单例具:有线程安全、代码量少、满足静态分析器的要求等优势,但是对单例类使用init、new等创建时,会导致出现多个类对象的问题,所以应做相关的安全处理,避免其他人调用时因为不规范调用引起问题。
单例一般使用还是蛮简单的,但涉及到一些骚操作的滥用时,还是会导致线程卡死、死锁等crash的,所以规范使用还是很重要的
关于@synchronized 和 dispatch_once的区别请参考
@synchronized 和 dispatch_once的区别