------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
一、单例的介绍
单例:在内存中只有唯一的实例,并且提供一个全局的访问方法。
OC中常见的单例有:
UIApplication
NSFileManager
NSUserdefaults
NotificationCenter
在IOS开发中,单例的应用非常广,甚至都有滥用的趋势。
二、单例的设计步骤
开发中有一个最常用的单例就是音乐或者音效播放,下面以音乐播放的例子介绍设计单例的思路和步骤:
1>新建一个类SoundTools继承NSObject
2>定义一个全局访问方法
<span style="background-color: rgb(255, 255, 255);">+ (instancetype)sharedSoundTools {
return [[self alloc] init];
}</span>
如果对象遵守了NSCopying协议,还需要实现copyWithZone方法
<span style="background-color: rgb(255, 255, 255);">- (id)copyWithZone:(NSZone *)zone {
id s = [[self.class allocWithZone:zone] init];
return s;
}</span>
3>编写ViewController的viewDidLoad方法中编写测试代码
<span style="background-color: rgb(255, 255, 255);">- (void)viewDidLoad {
[super viewDidLoad];
SoundTools *s1 = [[SoundTools alloc] init];
SoundTools *s2 = [[SoundTools alloc] init];
SoundTools *s3 = [SoundTools sharedSoundTools];
SoundTools *s4 = [s3 copy];
NSLog(@"%@ %@ %@ %@", s1, s2, s3, s4);
}</span>
4>运行,此时当然地址会不一样
运行效果:
可以看到,上面的四个实例化方法都会调用内存分配方法alloc或者allocWithZone,由于历史原因,alloc内部会调用allocWithZone方法。因此,只要保证allocWithZone方法每次返回的都是同一个实例,就可以让以上的四个方法返回同一个对象了!
5>添加一个全局的实例对象,修改allocWithZone方法
<span style="background-color: rgb(255, 255, 255);">static SoundTools *instance;
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
if (instance == nil) {
instance = [super allocWithZone:zone];
}
return instance;
}</span>
至此,我们已经实现了一个最简单的单例!
但是,如果涉及到多线程同时执行这段代码,可能会分配多个内存空间得到多个不同的实例!
6>修改allocWithZone方法
<span style="background-color: rgb(255, 255, 255);">+ (instancetype)allocWithZone:(struct _NSZone *)zone {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [super allocWithZone:zone];
});
return instance;
}</span>
注:里面的dispatch_once方法可以保证在多线程运行的情况下,block里面的代码只会被执行一次,所以可以删除之前的判断语句。
7>修改copyWithZone方法
<span style="background-color: rgb(255, 255, 255);">- (id)copyWithZone:(NSZone *)zone {
return instance;
}</span>
由于这是个对象方法,执行这个方法之前已经实例化了,所以可以直接返回实例化对象。
做个对比,如果我们将sharedSoundTools这个类方法也直接返回instance,会怎么样呢?
改变测试代码的执行顺序,将s3的实例化代码放在最前面,运行
发现s3和s4都是空,这是因为执行这个类方法时,还没有实例化的对象存在!
8>修改sharedSoundTools方法
<span style="background-color: rgb(255, 255, 255);">+ (instancetype)sharedSoundTools {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}</span>
注:为什么这里也要使用dispatch_once呢?如果不使用,会发生什么状况?instance可能会被多次初始化,因为前面只是保证了只alloc一次,并没有保证只init一次。这样,一个完整的单例基本上就实现了。
细心的人可能会发现,当我们不调用sharedSoundTools方法,而是调用[[SoundTools alloc] init],还是可以实例化多次,如果要解决这个问题,就需要重写init方法,在init方法中再加一个dispatch_once。这样是不好的,因为静态区的资源是有限的,使用太多锁会造成资源的浪费。因此,单例规定:所有对单例的访问,都用share(或者default,strandard)方法。