在开发中经常会用到单例设计模式,目的就是为了在程序的整个生命周期内,只会创建一个类的实例对象,而且只要程序不被杀死,该实例对象就不会被释放。下面我们来看看单例的概念、用途、如何创建,以便加深理解。
有两种方法来创建单例,下面分别介绍
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; }
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和同步锁创建单例会出现什么问题,去掉两者后创建单例的代码如下
+ (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。
此时就创建了两个实例对象,导致问题。
dispatch once保证程序在运行过程中只会被运行一次,那么假设此时线程1先执行shareInstance方法,创建了一个实例对象,线程2就不会再去执行dispatch once的代码了。从而保证了只会创建一个实例对象。
假设此时线程1在执行shareInstance方法,那么synchronize大括号内创建单例的代码,如下所示:
if (_instance == nil) { _instance = [[self alloc] init]; }
就会被当做一个任务被加上了一把锁。此时假设线程2也想执行shareInstance方法创建单例,但是看到了线程1加的互斥锁,就会进入睡眠模式。等到线程1执行完毕,才会被唤醒,然后去执行上面所示的创建单例的代码,但是此时_instance !=nil,所以不会再创建新的实例对象了。从而保证只会创建一个实例对象。
但是互斥锁会影响性能,所以最好还是使用GCD方式创建单例。
如果我们需要在程序中创建多个单例,那么需要在每个类中都写上一次上述代码,非常繁琐。
我们可以使用宏来封装单例的创建,这样任何类需要创建单例,只需要一行代码就搞定了。
Singleton.h文件 ================================== #define SingletonH(name) + (instancetype)shared##name; #define SingletonM(name) / static id _instance; / / + (instancetype)allocWithZone:(struct _NSZone *)zone / { / static dispatch_once_t onceToken; / dispatch_once(&onceToken, ^{ / _instance = [super allocWithZone:zone]; / }); / return _instance; / } / / + (instancetype)shared##name / { / 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; / }
假设我们要在类viewcontroller中使用,调用方法如下:
viewcontroller.h文件 =========================== #import <UIKit/UIKit.h> #import "Singleton.h" @interface ViewController : UIViewController SingletonH(viewController) @end viewcontroller.m文件 =========================== @interface ViewController () @end @implementation ViewController SingletonM(ViewController) - (void)viewDidLoad { [super viewDidLoad]; NSLog(@"%@ %@ %@ %@", [ViewController sharedViewController],[ViewController sharedViewController], [[ViewController alloc] init],[[ViewController alloc] init]); } @end
<ViewController: 0x7f897061bc00> <ViewController: 0x7f897061bc00> <ViewController: 0x7f897061bc00> <ViewController: 0x7f897061bc00>
可以看到四个对象的内存地址完全一样,说明是同一个对象