@synchronized和dispatch_once 在单例的使用如下:
static LvSingleClass * singleClass = nil;
+ (LvSingleClass *)sharedSingleClass {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
singleClass = [[LvSingleClass alloc] init];
});
return singleClass;
}
+ (LvSingleClass *)sharedSingleClass {
@synchronized(self){
if (singleClass == nil) {
singleClass = [[LvSingleClass alloc] init];
}
}
return singleClass;
}
在OC中,@synchronize 是用NSRecursiveLock实现的,并且隐式添加了exception handler,如果有异常抛出,exception handler 会自动释放互斥锁。
而dispatch_once 它省去了锁的操作,用的是大量的原子操作,该原子操作内部不是靠pthread等锁来实现的,而是利用lock的汇编指令,靠底层cpu指令来执行的 ,所以拥有高的性能,
//代码测试
CFAbsoluteTime dispatchStartTime = CFAbsoluteTimeGetCurrent();
for (int i = 0;i < 1000;i ++) {
[SingleClass sharedSingle];
}
CFAbsoluteTime dispatchEndTime = CFAbsoluteTimeGetCurrent();
NSLog(@"dispatch time:%f s",dispatchEndTime - dispatchStartTime);
CFAbsoluteTime synchronizeStartTime = CFAbsoluteTimeGetCurrent();
for (int i = 0;i < 1000 ;i++) {
[SingleOneClass sharedSingle];
}
CFAbsoluteTime synchronizedEndTime = CFAbsoluteTimeGetCurrent();
NSLog(@"synchronized time : %f s",synchronizedEndTime - synchronizeStartTime);
//测试结果
2018-10-31 14:49:22.313217+0800 Demo[20364:6801421] dispatch time:0.000047 s
2018-10-31 14:49:22.313530+0800 Demo[20364:6801421] synchronized time : 0.000116 s
可以发现 在单线程中可以发现dispatch_once方法的性能要明显优于synchronized方法,所以在实际的应用中我们可以多采用dispatch_once方式来实现单例。
-
下面来简单了解下 @synchronized
@synchronized指令的对象是用于区分受保护块的唯一标识符。如果在两个不同的线程中执行上述方法,则anObj在每个线程上为参数传递一个不同的对象,每个线程都会锁定并继续处理,而不会被另一个阻塞。但是,如果在两种情况下都传递相同的对象,则其中一个线程将首先获取锁定,另一个线程将阻塞,直到第一个线程完成关键部分。
作为预防措施,该@synchronized块隐式地向受保护代码添加异常处理程序。如果抛出异常,此处理程序会自动释放互斥锁。这意味着为了使用该@synchronized指令,还必须在代码中启用Objective-C异常处理。如果您不希望由隐式异常处理程序引起额外开销,则应考虑使用锁类。 -
下面来简单了解下 dispatch_once
static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ //some code });
onceToken 参数它保证了后面的 block 只被执行一遍。我们通过一个简单的单例实现来看看 onceToken 在 dispatch_once 运行过程中的变化:
+ (SingleClass *)sharedSingle { static SingleClass * singleClass = nil; static dispatch_once_t onceToken; NSLog(@"Before onceToken = %ld", onceToken); dispatch_once(&onceToken, ^{ singleClass = [[SingleClass alloc] init]; NSLog(@"running onceToken = %ld", onceToken); }); NSLog(@"After onceToken = %ld", onceToken); return singleClass; }
运行结果
2018-10-31 16:05:05.306434+0800 Demo[20530:7311130] Before onceToken = 0 2018-10-31 16:05:05.306574+0800 Demo[20530:7311130] running onceToken = 1024 2018-10-31 16:05:05.306736+0800 Demo[20530:7311130] After onceToken = -1
通过输出我们可以发现,在 dispatch_once 执行前,onceToken 的值是 0,因为 dispatch_once_t 是由 typedef long dispatch_once_t 而来,所以在 onceToken 还没被手动赋值的情况下,0 是编译器给 onceToken 的初始化赋值。在 dispatch_once 执行过程中,onceToken 是一个很大的数字,这个值是 dispath_once 内部实现中一个局部变量的地址,并不是一个固定的值。当 dispatch_once 执行完毕,onceToken 的值被赋为 -1。