工欲善其事,必先利其器。
通常我们在实现单例时候都会使用synchronized或者dispatch_once方法,初始化往往是下面的样子:
使用synchronized方法实现:
1 2 3 4 5 6 7 8 9 10 |
static id obj = nil; +(instancetype)shareInstance { @synchronized(self) { if (!obj) { obj = [[SingletonObj alloc] init]; } } return obj; } |
使用dispatch_once方法实现:
1 2 3 4 5 6 7 8 9 |
static id obj = nil; +(instancetype)shareInstance { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ obj = [[SingletonObj alloc] init]; }); return obj; } |
性能差异
上面的这些写法大家应该都很熟悉,既然两种方式都能实现,我们来看看两者的性能差异,这里简单写了个测试的demo,使用两个方法分单线程跟多线程(采用dispatch_apply方式,性能相对较高)去访问一个单例对象一百万次,对比这期间的耗时,从iPod跟5s测试得到如下的结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
//ipod,主线程 SingletonTest[4285:446820] synchronized time cost:2.202945s SingletonTest[4285:446820] dispatch_once time cost:0.761034s //5s,主线程 SingletonTest[5372:2394430] synchronized time cost:0.466293s SingletonTest[5372:2394430] dispatch_once time cost:0.070822s //ipod,多线程 SingletonTest[4315:448499] synchronized time cost:3.385109s SingletonTest[4315:448499] dispatch_once time cost:0.908009s //5s,多线程 SingletonTest[5391:2399069] synchronized time cost:0.507504s SingletonTest[5391:2399069] dispatch_once time cost:0.169934s |
可以发现dispatch_once方法的性能要明显优于synchronized方法(多线程不采用dispathc_apply方式差距更明显),所以在实际的应用中我们可以多采用dispatch_once方式来实现单例。通常使用的时候了解这些就够了,不过想知道两者的具体差异就需要我们再迈进一步。
深入@synchronized(object)
翻看苹果的文档可以发现 @synchronized指令内部使用锁来实现多线程的安全访问,并且隐式添加了一个异常处理的handler,当异常发生时会自动释放锁。在stackoverflow上看到@synchronized指令其实可以转换成objc_sync_enter跟objc_sync_exit,可以在头文件中找到这两个函数:
1 2 3 4 5 |
//Allocates recursive pthread_mutex associated with 'obj' if needed int objc_sync_enter(id obj) //End synchronizing on 'obj' int objc_sync_exit(id obj) |
根据注释文档,objc_sync_enter会根据需要给每个传进来的对象创建一个互斥锁并lock,然后objc_sync_exit的时候unlock,这样就可以通过这个锁来实现多线程的安全访问,所以结合苹果文档可以认为
1 2 3 |
@synchronized(self) { //thread safe code } |
等价于
1 2 3 4 5 6 |
<a |
<a
使用dispatch_once方法实现:
性能差异上面的这些写法大家应该都很熟悉,既然两种方式都能实现,我们来看看两者的性能差异,这里简单写了个测试的demo,使用两个方法分单线程跟多线程(采用dispatch_apply方式,性能相对较高)去访问一个单例对象一百万次,对比这期间的耗时,从iPod跟5s测试得到如下的结果
可以发现dispatch_once方法的性能要明显优于synchronized方法(多线程不采用dispathc_apply方式差距更明显),所以在实际的应用中我们可以多采用dispatch_once方式来实现单例。通常使用的时候了解这些就够了,不过想知道两者的具体差异就需要我们再迈进一步。 深入@synchronized(object)翻看苹果的文档可以发现 @synchronized指令内部使用锁来实现多线程的安全访问,并且隐式添加了一个异常处理的handler,当异常发生时会自动释放锁。在stackoverflow上看到@synchronized指令其实可以转换成objc_sync_enter跟objc_sync_exit,可以在头文件中找到这两个函数:
根据注释文档,objc_sync_enter会根据需要给每个传进来的对象创建一个互斥锁并lock,然后objc_sync_exit的时候unlock,这样就可以通过这个锁来实现多线程的安全访问,所以结合苹果文档可以认为
等价于
|