第21章 有些类也需要计划生育--单例模式
21.3 生还是不生是自己的责任
21.4 单例模式(Singleton),保证一个类仅有一个实例,并提供一个访问它的全局访问点。
通常我们可以设置一个全局变量使得这一个对象被访问,但它不能防止你实例化多个对象。一个最好的办法是,让类自身负责保存它的唯一实例。这个类可以保证没有其他实例被创建,并且它可以提供一个访问该实例的方法。
21.5 多线程时的单例
lock是确保当一个线程位于代码的临界区时,另一个线程不进入临界区。如果其它线程试图进入锁定的代码,则他将一直等待(即被阻塞),知道该对象被释放。
class Singleton
{
private static Singleton instance;
private static readonly object syncRoot = new object();
private Singleton()
{
}
public static Singleton GetInstance()
{
if(instance == null) //1 非空判断
{
lock(syncRoot)
{
if(instance == null) //2 非空判断
{
instance = new Singleton();
}
}
}
return instance;
}
}
注意看此处有两个非空判断:
如两个线程,两个线程可能同时通过判断1,若线程1先通过,线程1通过后实例化了对象,然后你会发现如果没有判断2,线程2也会实例化一个对象,这就不是单例了。
IOS单例模式:
1:使用dispatch_once_t;
+ (AccountManager *)sharedManager {
static AccountManager *sharedAccountManagerInstance = nil;
static dispatch_once_t predicate; dispatch_once(&predicate, ^{
sharedAccountManagerInstance = [[self alloc] init];
});
return sharedAccountManagerInstance;
}
2:使用@synchronized
+ (id)sharedInstance
{
static Instance *obj = nil;
@synchronized([Instance class])
{
if(!obj)
obj = [[Instance alloc] init];
}
return obj;
}
static dispatch_once_t once;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"------0");
[NSThread sleepForTimeInterval:1.0];
NSLog(@"------1");
dispatch_once(&once, ^{
NSLog(@"------2");
});
NSLog(@"------3");
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"------4");
dispatch_once(&once, ^{
NSLog(@"------5");
[NSThread sleepForTimeInterval:2.0];
NSLog(@"------6");
});
NSLog(@"------7");
});
/* 40/04 5 1 6 37/73
2016-06-29 18:36:43.501 GCD[1679:122152] ------4
2016-06-29 18:36:43.501 GCD[1679:122153] ------0
2016-06-29 18:36:43.502 GCD[1679:122152] ------5
2016-06-29 18:36:44.504 GCD[1679:122153] ------1
2016-06-29 18:36:45.504 GCD[1679:122152] ------6
2016-06-29 18:36:45.505 GCD[1679:122152] ------7
2016-06-29 18:36:45.505 GCD[1679:122153] ------3
*/
dispatch_once不仅仅保证临界区域的代码只执行一遍,还可以保证多个线程同时到达临界区域时,只有一个线程可以进入,其它会被阻塞。