ios创建单例中的@synchronized和dispatch_once

@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。

猜你喜欢

转载自blog.csdn.net/u013983033/article/details/83581841