单例是一种常见的设计模式。经过单例模式能够保证系统中一个类只有一个实例并且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。设计模式
在单例所属的类中只存在这么一个实例,而且相似全局变量,在系统任意位置都能访问单例对象。多线程
1)在系统中某种对象只能存在一个,多了不行。性能
2)系统中某种对象实例只须要一个就够了,节省内存。ui
[UIApplication sharedApplication];
[NSNotificationCenter defaultCenter];
[NSFileManager defaultManager];
[NSUserDefaults standardUserDefaults];
[NSURLCache sharedURLCache];
[NSHTTPCookieStorage sharedHTTPCookieStorage];复制代码
咱们知道对于单例类
,咱们必须留出一个接口来返回生成的单例,因为一个类中只能有一个实例,因此咱们在第一次访问这个实例的时候建立,以后访问直接取已经建立好的实例spa
@implementation Singleton
+ (instancetype)shareInstance
{
static Singleton* single;
if (!single) {
single = [[Singleton alloc] init];
}
return single;
}
@end
复制代码
ps:严格意义上来讲,咱们还须要将
alloc
方法封住,由于严格的单例是不容许再建立其余实例的,而alloc
方法能够在外部任意生成实例。可是考虑到alloc
属于NSObject,iOS中没法将alloc
变成私有方法,最多只能覆盖alloc
让其返回空,不过这样作也可能会让使用接口的人误解,形成其余问题。因此咱们通常状况下对alloc
不作特殊处理。系统的单例也未对alloc
作任何处理线程
对于一个实例,咱们通常并不能保证他必定会在单线程模式下使用,因此咱们得适配多线程状况。在多线程状况下,上面的单例建立方式可能会出现问题。若是两个线程同时调用shareInstance
,可能会建立出2个single来。因此对于多线程状况下,咱们须要使用@synchronized
来加锁。设计
@implementation Singleton
+ (instancetype)shareInstance
{
static Singleton* single;
@synchronized(self){
if (!single) {
single = [[Singleton alloc] init];
}
}
return single;
}
@end
复制代码
这样的话,当多个线程同时调用shareInstance
时,因为@synchronized
已经加锁,因此只能有一个线程进入建立single
。这样就解决了多线程下调用单例的问题code
使用@synchronized
虽然解决了多线程的问题,可是并不完美。由于只有在single
未建立时,咱们加锁才是有必要的。若是single
已经建立.这时候锁不只没有好处,并且还会影响到程序执行的性能(多个线程执行@synchronized
中的代码时,只有一个线程执行,其余线程须要等待)。那么有没有方法既能够解决问题,又不影响性能呢?
这个方法就是GCD中的dispatch_once对象
@implementation Singleton
+ (instancetype)shareInstance
{
static Singleton* single;
static dispatch_once_t onceToken; //①onceToken = 0;
dispatch_once(&onceToken, ^{
NSLog(@"%ld",onceToken); //②onceToken = 140734731430192
single = [[Singleton alloc] init];
});
NSLog(@"%ld",onceToken); //③onceToken = -1;
return single;
}
@end
复制代码
dispatch_once
为何能作到既解决同步多线程问题又不影响性能呢?
下面咱们来看看dispatch_once
的原理:继承
dispatch_once
主要是根据onceToken
的值来决定怎么去执行代码。
onceToken
= 0时,线程执行dispatch_once
的block
中代码onceToken
= -1时,线程跳过dispatch_once
的block
中代码不执行onceToken
为其余值时,线程被线程被阻塞,等待onceToken
值改变当线程首先调用shareInstance
,某一线程要执行block
中的代码时,首先须要改变onceToken
的值,再去执行block中的代码。这里onceToken
的值变为了140734731430192。
这样当其余线程再获取onceToken
的值时,值已经变为140734731430192。其余线程被阻塞。
当block
线程执行完block
以后。onceToken
变为-1。其余线程再也不阻塞,跳过block
。
下次再调用shareInstance
时,block已经为-1。直接跳过block
。
这样dispatch_once
在首次调用时同步阻塞线程,生成单例以后,再也不阻塞线程。dispatch_once
是建立单例的最优方案
dispatch_once
方法,这样便可解决多线程问题,又能达到高效的目的单例虽然好用,不过他并不适合继承和扩展,因此使用单例的时候要注意这点。千万不要任何东西都使用单例,要适可而止