系列:iOS开发-单例模式
在我们做开发的时候经常会遇到需要在很多地方调用同一个对象的现象,
比如在一个类中,我们可能会用到全局变量,在一个方法中会重复用到某个局部变量...
今天要说的单例模式就是一个类似这样的存在
- 单例模式的作用 :可以保证在程序运行过程,一个类只有一个实例,而且该实例易于供外界访问,从而方便地控制了实例个数,并节约系统资源.
- 单例模式的使用场合:在整个应用程序中,共享一份资源(这份资源只需要创建初始化1次),应该让这个类创建出来的对象永远只有一个. 即一旦你创建了一个单例类,不论你在多少个界面中初始化调用了这个单例方法取得对象,它们所有的对象都是指向的同一块内存存储空间(即单例类保证了该类的实力对象是唯一存在的一个).
在iOS中开发中,单例模式是最常使用的设计模式之一。单例模式不需要传递任何参数,就有效地解决了不同代码间的数据共享问题。单例类是一个非常重要的概念,因为它们表现出了一种十分有用的设计模式。单例类的应用贯穿于整个iOS的SDK中。例如,UIApplication类有一个方法叫sharedApplication,从任何地方调用这个方法,都将返回与当前正在运行的应用程序相关联的UIApplication实例。除了这个,NSNotificationCenter(消息中心) 、NSFileManager(文件管理) 、 NSUserDefaults(持久化存储数据) 、NSURLCache(请求缓存)、NSHTTPCookieStorage(应用程序cookies池)都是系统单例;
总而言之,单例类保证了应用程序的生命周期中有且仅有一个该类的实例对象,而且易于外界访问。
那么我们来看看他的设计思路应该是什么?
- 永远只分配一块内存来创建对象
- 提供一个类方法,返回内部唯一的一个变量
- 最好保证init方法也只初始化一次
因为只有这样才能保证无论在任何时候创建这个类的对象,都应该是固定的一个
OK,我们既然有思路了,我们应该怎么创建呢?
我们先看看一个普通的类的init方法
- (instancetype)init{
self = [super init];
return self;
}
但是这个是实例方法,我们需要改成类方法,所以我们仿照着创建一个方法
static DemoSingleton *_instance = nil;
+(DemoSingleton *)shareDemoSingleton{
if (_instance == nil) {
_instance = [[self alloc]init];
}
return _instance;
}
首先定义一个全局变量,接着在创建方法中做判断,如果有就直接返回,没有就创建之后返回,那这个就是一个有效的单例的创建模式.
我们打印看看
我们发现两次创建的对象的地址都是同一个,那代表什么意思呢?是的,这就是一个单例对象,我们可以在任何地方调用这个类方法来得到这个对象,它活跃于整个应用的周期.
但是学过并发的我们可能会想到一点,如果开始没有,然后有两个地方同时创建,会怎么样呢?是的,这样就会导致创建两个不一样的对象,那么久还打不到我们要的效果,
于是有了新的方法,我们加一个锁
+(DemoSingleton *)shareDemoSingleton{
NSLock *lock = [[NSLock alloc]init];
[lock lock];
if (_instance == nil) {
_instance = [[self alloc]init];
}
[lock unlock];
return _instance;
}
或者
static DemoSingleton *instance = nil;
+(DemoSingleton *)shareDemoSingleton{
@synchronized (self) {
if (_instance == nil) {
_instance = [[self alloc]init];
}
return _instance;
}
}
当然我们后期也会学到iOS中的GCD,我们也可以使用这中方法
static DemoSingleton *_instance = nil;
+(DemoSingleton *)shareDemoSingleton{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[self alloc]init];
});
return _instance;
}
大家都可以尝试看看,效果都是一样的,主要的目的只有一个,加锁,防止同时创建,
当然对于每个类,iOS本身都提供一个类方法创建+(instancetype)allocWithZone:(struct _NSZone *)zone
所以我们为了严谨,也需要在这个地方写上同样的代码
+(instancetype)allocWithZone:(struct _NSZone *)zone{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [super allocWithZone:zone];
});
return _instance;
}
不仅仅如此,我们还有copyWithZone 和 mutableCopyWithZone
我们需要修改下
-(id)copyWithZone:(NSZone *)zone
{
return _instance;
}
-(id)mutableCopyWithZone:(NSZone *)zone
{
return _instance;
}
我们发现无论是创建,还是拷贝等,永远都只有一个对象了,
那么这个就是我们要的单例效果了,
但是我们是不是每一次创建单例都需要这么复杂呢?
我们可以写一个宏,
// @interface
#define singleton_interface(className) \
+ (instancetype)shared##className;
// @implementation
#define singleton_implementation(className) \
static className *_instance; \
+ (instancetype)shared##className \
{ \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instance = [[self alloc] init]; \
}); \
return _instance; \
} \
+ (instancetype)allocWithZone:(NSZone *)zone \
{ \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instance = [super allocWithZone:zone]; \
}); \
return _instance; \
} \
- (instancetype)copyWithZone:(NSZone *)zone{ \
return _instance; \
} \
- (instancetype)mutableCopyWithZone:(NSZone *)zone{ \
return _instance; \
}
看上去有点复杂?
OK 引入.h文件,只要写两行,所有的就搞定了.
我们同样运行看看
是不是一样的结果?
OK ,以上是单例的写法,
那么单例的作用是什么呢?
简单举个例子,比如我开发的是一个音乐播放软件,那么我会想到的是我会创建一个音乐播放器的类 它有几个方法,比如播放,暂停,快进,上下曲等...
另外我还得保证我整个应用就只有一个这样的播放器,所以我在整个应用任何地方调用播放器的方法的时候都需要保证我操作的是那唯一的播放器,否则就会出现问题.那么此时,我就很适合使用单例模式,这个播放器类写成一个单例,这样无论是在什么地方创建,获取或着拷贝等,永远都是那个正在使用的播放器...
当然还有,比如我有很多信息数据是需要在整个应用都存在,也是唯一的,比如用户信息,我也可以写成单例,方便任何地方调用,(当然也可以写到数据库或者沙盒中,或者其他方式...)
iOS在开发中,给我们提供了几个自带的单例,不要认为它没有什么作用,比如NSUserDefaults,我们做持久化就要保证的唯一要求就是写在一个地方,读取那肯定还要在那个地方,否则就读取不到了,之所以写成了单例,本身的要求其实就是唯一性.
OK,单例只是一种设计模式,本身并不复杂,他还是一个类,任何处理都是一个普通的类,只是所有的方法操作的对象是唯一而已.