iOS开发之单例模式

iOS开发之单例

单例介绍

1.什么是单例

单例模式是一种常用的软件设计模式。在它的核心结构中包含一个被称为单例类的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果系统在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。

2.单例用处

应用场景:
确保程序运行期某个类,只有一份实例,用于进行资源共享控制。
优势:
使用简单,延时求值,易于跨模块
iOS的系统中用到的一些单例

1
2
3
[UIApplication sharedApplication];
[NSUserDefaults standardUserDefaults];
[NSURLCache sharedURLCache];

iOS单例的创建

1.单线程单例

单例类需要保证只有一个实例,因此在第一次访问这个实例的时候才创建,之后访问直接取已经创建好的实例

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
+(instancetype)shareInstance
{
static Singleton *singleteon;
if (!singleteon ) {
singleteon = [[Singleton alloc] init];
}
return singleteon;
}
```
单线程单例存在一些弊端,在多线程的情况下,会产生线程不安全的情况。严格意义上来说,我们还需要把alloc方法变为私有方法才行,严格的单例是不允许再创建其他实例的,而alloc方法可以在外部任意生成实例。但是考虑到**alloc**属于NSObject,iOS中无法将**alloc**变成私有方法,最多只能覆盖**alloc**让其返回空。不过个人不建议这么做,一般情况下对**alloc**不做特殊处理。系统的单例也未对**alloc**做任何处理
<!--more-->
## 2.@synchronized单例
上面单线程单例,在多线程情况下,可能会出现一些问题。如果两个线程同时调用**shareInstanc**,可能会创建出2个singleton出来。所以在对多线程情况下,我们需要使用**@synchronize**来加锁
``` objc
+(instancetype)shareInstance
{
static Singleton *singleton;
@synchronized (self) {
if (!singleton) {
singleton = [[Singleton alloc] init];
}
}
return singleton;
}
```
加锁以后,当多个线程同时调用shareInstance时,由于**@synchronized**已经加锁,只能有一个线程创建singleton实例。这样就解决了多线程调用单例的问题。
## 3.dispatch_once单例
使用**@synchronized**虽然一定程度上解决了多线程的问题,但并不完美。因为只有在**singleton**未创建时,加锁才是必要的。如果**singleton**已经创建,这个时候还加锁的话,会影响性能。
在iOS中,GCD为我们提供方便又高效的方法---**dispatch_once**
``` objc
+(instancetype)shareInstance
{
static Singleton *singleton;
static dispatch_once_t onceToken; //1.onceToken = 0;
dispatch_once(&onceToken,^{
NSLog(@"%ld",onceToken); //2.onceToken = 140734537148864
singleton = [[Singleton alloc] init];
});
NSLog(@"%ld",onceToken); //3.onceToken = -1
return singleton;
}

dispatch_once为什么能做到既解决同步多线程问题

dispatch_once的原理:
dispatch_once主要是根据onceToken的值来决定怎么去执行代码。
1.当onceToken = 0时,线程执行dispatch_onceblock中代码
2.当onceToken = -1时,线程跳过dispatch_onceblock中代码不执行
3.当onceToken为其他值时,线程被阻塞,等待onceToken值改变
当线程调用shareInstance,此时onceToken = 0,调用block中的代码,此时onceToken的值变为140734537148864。当其他线程再调用shareInstance方法时,onceToken的值已经是140734537148864了,线程阻塞。当block线程执行完block之后,onceToken变为-1.其他线程不再阻塞,跳过block。下次再调用shareInstance时,block已经为-1.直接跳过block
dispatch_once设计挺巧妙的。

感谢!我会继续努力,谢谢!