知识点
- 1、什么是单例
- 2、单例的好处
- 3、单例创建方式
- 4、封装”单例的创建”
- 5、使用单例的封装实现单例
- 6、使用单例
1、What’s 单例 ?
顾名思义,单例就是一个类只有一个实例对象。确保在程序过程中,无论创建多少次对象,该对象都是同一个实例,都指向同一块存储空空间。在设计模式中单例模式也是很常用的模式。
单例模式的三个要点
- 该类只能有一个实例;
- 它必须自行创建这个实例;
- 必须向外界提供这个实例供调用者调访问。
2、Benefit of 单例
- 可以保证在程序运行过程中,一个类只有一个实例,而且该实例易于供外界访问
- 方便的控制了实例个数,并且大大节约系统资源
单例模式的使用场合:在整个应用程序中,共享一份资源(这份资源只需要创建初始化一次)
3、The ways to create 单例
创建方式
a) 利用手动添加线程锁
@synchronized
控制
首先要在类中申明一个私有的静态实例,保证该类被循环引用,这样单例才会一直存在不会被释放。static id _instance;
要想实例单例,就必须控制类的构造方法,因此重写
allocWithZone
方法。+ (instancetype)allocWithZone:(struct _NSZone *)zone { @synchronized (self) { if(!_instance) { _instance = [super allocWithZone:zone]; } } return _instance; }
同理,要达到实现单例,按照苹果提供的
[UIApplication sharedApplication]
方式获取单例,需要提供一个sharedInstance的类方法去访问单例。因此需要在类的.h文件中声明工厂方法,并在.m文件中实现。+ (instancetype)sharedInstance;
+ (instancetype)sharedCar { @synchronized (self) { if(!_instance) { _instance = [[self alloc] init]; } } return _instance; }
当然,有时候我们会用到一个实现了
NSCopying
的copy
对象方法,在单例中应该重写该方法,使得赋值的实例仍然是唯一的实例。- (id)copyWithZone:(NSZone *)zone { return _instance; }
b) 通过利用
GCD
中的dispatch_once
方法控制实例个数
和用线程同步锁一样的道理,要想达到单例的效果,必须控制外界创建实例的方法,而GCD的方式也不例外,只是在控制的方式上利用用dispatch_once
在程序运行过程中只会执行一次
的特性实现,并且dispatch_once
方法是线程安全的。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; }
4、封装”单例的创建”
从上面的代码我们可以看出,要想实现一个单例是很容易的。同时也可以看出实现单例的代码都是差不多一样的,除了可能在类的工厂方法的名字可能采用instance***
以为,其余的代码都是一模一样,而这样的代码重复工作量,在我们进行开发的过程中是没有多大意思并且花费时间,因此可以借用宏的特性将单例的实现进行封装,只要每次在.h文件中引入宏,并且调用宏的方法就可以实现单例。那么我不啰嗦了,直接上代码。
1. 首先需要创建一个.h文件MRSingleton.h
(当然该文件名可以根据个人喜好取名),在.h文件中进行宏的定义。
// .h 文件中需要声明的方法宏定义
#define MRSingletonH(name) + (instancetype)shared##name;
// .m 文件中需要的方法宏定义
#define MRSingletonM(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;\
}
注意!!!
需要特别注意的是上面的宏定义中的\
是一定不能获缺的,因为编译器在检测宏时默认只会认为define所在一行才是宏的定义内容,因此只会将所在行的代码进行宏的替换,\
的的作用就是告诉编译器后面一行的内容仍然是宏需要替换的代码。
5、使用单例的封装实现单例
1 在声明文件.h文件中导入宏定义文件
MRSingleton.h
#impport "MRSingleton.h"
- 2 在.h文件中调用宏的方法
MRSingletonH(类名)
(在MRSingletonH后括号中填入类名, 生成方法instanceMRPerson
的方法声明)
@interface MRPerson : NSObject
// 调用宏声明单例方法
MRSingletonH(Person)
@end
- 3 在.h 文件中调用宏的方法
MRSingletonH(类名)
@interface MRPerson ()<NSCopying>
@end
@implementation MRPerson
// 调用宏实现单例方法
MRSingletonM(MRPerson)
@end
6、单例使用
#pragma mark --- 利用GCD实现单例
// 原始方法创建
MRPerson *person2 = [[MRPerson alloc] init];
// 单例方法创建
MRPerson *person1 = [MRPerson sharedPerson];
// copy
MRPerson *person3 = [person2 copy];
NSLog(@"[MRPerson sharedPerson]---%@", person1);
NSLog(@"[[MRPerson alloc] init]---%@", person2);
NSLog(@"[person2 copy]---%@", person3);
ViewController *viewController1 = [ViewController sharedViewController];
ViewController *viewController2 = [[ViewController alloc] init];
ViewController *viewController3 = [viewController2 copy];
NSLog(@"[ViewController sharedViewController]---%@", viewController1);
NSLog(@"[[ViewController alloc] init]---%@", viewController2);
NSLog(@"[viewController2 copy]---%@", viewController3);
2016-06-26 20:26:10.903 单例模式-OC[3818:214382] [MRPerson sharedPerson]---<MRPerson: 0x7bf2cdb0>
2016-06-26 20:26:10.904 单例模式-OC[3818:214382] [[MRPerson alloc] init]---<MRPerson: 0x7bf2cdb0>
2016-06-26 20:26:10.904 单例模式-OC[3818:214382] [person2 copy]---<MRPerson: 0x7bf2cdb0>
2016-06-26 20:26:10.904 单例模式-OC[3818:214382] [ViewController sharedViewController]---<ViewController: 0x7bf29c40>
2016-06-26 20:26:10.904 单例模式-OC[3818:214382] [[ViewController alloc] init]---<ViewController: 0x7bf29c40>
2016-06-26 20:26:10.904 单例模式-OC[3818:214382] [viewController2 copy]---<ViewController: 0x7bf29c40>
可以看出单例已经成功创建,每一个实例对象都指向同一块内存空间!!!(ps:这篇是OC版,后续会用swift实现)