在开发中经常会用到单例设计模式,目的就是为了在程序的整个生命周期内,只会创建一个类的实例对象,而且只要程序不被杀死,该实例对象就不会被释放。下面我们来看看单例的概念、用途、如何创建,以便加深理解。
作用
- 在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。比如在APP开发中我们可能在任何地方都要使用用户的信息,那么可以在登录的时候就把用户信息存放在一个文件里面,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理。
- 有的情况下,某个类可能只能有一个实例。比如说你写了一个类用来播放音乐,那么不管任何时候只能有一个该类的实例来播放声音。再比如,一台计算机上可以连好几个打印机,但是这个计算机上的打印程序只能有一个,这里就可以通过单例模式来避免两个打印任务同时输出到打印机中,即在整个的打印过程中我只有一个打印程序的实例。
创建单例
有两种方法来创建单例,下面分别介绍
1、GCD方式创建单例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
static id _instance; + (instancetype)allocWithZone:(struct _NSZone *)zone { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _instance = [super allocWithZone:zone]; }); return _instance; } + (instancetype)sharedInstance { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _instance = [[self alloc] init]; }); return _instance; } - (id)copyWithZone:(NSZone *)zone { return _instance; } - (id)mutableCopyWithZone:(NSZone *)zone { return _instance; } |
2、互斥锁方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
static id _instance; + (instancetype)allocWithZone:(struct _NSZone *)zone { @synchronized(self) { if (_instance == nil) { _instance = [super allocWithZone:zone]; } } return _instance; } + (instancetype)sharedInstance { @synchronized(self) { if (_instance == nil) { _instance = [[self alloc] init]; } } return _instance; } - (id)copyWithZone:(NSZone *)zone { return _instance; } |
上面两种方式都可以创建单例,而且保证了用户不管是通过shareInstance方法,还是alloc、copy方法得到的实例都是一样的。
上面代码的关键之处就在于如何在多线程情况下保证创建的单例还是同一个。
我们先看看在GCD情况下,如果不使用dispatch_once和同步锁创建单例会出现什么问题,去掉两者后创建单例的代码如下
1 2 3 4 5 6 7 |
+ (instancetype)sharedInstance { if (_instance == nil) { _instance = [[self alloc] init]; } } |
假设此时有两条线程:线程1和线程2,都在调用shareInstance方法来创建单例,那么线程1运行到if (_instance == nil)
出发现instance = nil,那么就会初始化一个instance,假设此时线程2也运行到if的判断处了,此时线程1还没有创建完成实例instance,所以此时instance = nil还是成立的,那么线程2又会创建一个_instace。
此时就创建了两个实例对象,导致问题。
解决办法1、使用dispatch_once
dispatchonce保证程序在运行过程中只会被运行一次,那么假设此时线程1先执行shareInstance方法,创建了一个实例对象,线程2就不会再去执行dispatchonce的代码了。从而保证了只会创建一个实例对象。
解决办法2、使用互斥锁
假设此时线程1在执行shareInstance方法,那么synchronize大括号内创建单例的代码,如下所示:
1 /h3>
创建单例有两种方法来创建单例,下面分别介绍 1、GCD方式创建单例
2、互斥锁方式
上面两种方式都可以创建单例,而且保证了用户不管是通过shareInstance方法,还是alloc、copy方法得到的实例都是一样的。 上面代码的关键之处就在于如何在多线程情况下保证创建的单例还是同一个。 我们先看看在GCD情况下,如果不使用dispatch_once和同步锁创建单例会出现什么问题,去掉两者后创建单例的代码如下
假设此时有两条线程:线程1和线程2,都在调用shareInstance方法来创建单例,那么线程1运行到 此时就创建了两个实例对象,导致问题。 解决办法1、使用dispatch_oncedispatchonce保证程序在运行过程中只会被运行一次,那么假设此时线程1先执行shareInstance方法,创建了一个实例对象,线程2就不会再去执行dispatchonce的代码了。从而保证了只会创建一个实例对象。 解决办法2、使用互斥锁假设此时线程1在执行shareInstance方法,那么synchronize大括号内创建单例的代码,如下所示: |