在开发中经常会用到单例设计模式,目的就是为了在程序的整个生命周期内,只会创建一个类的实例对象,而且只要程序不被杀死,该实例对象就不会被释放。
在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。比如在APP开发中我们可能在任何地方都要使用用户的信息,那么可以在登录的时候就把用户信息存放在一个文件里面,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理。
有的情况下,某个类可能只能有一个实例。比如说你写了一个类用来播放音乐,那么不管任何时候只能有一个该类的实例来播放声音。再比如,一台计算机上可以连好几个打印机,但是这个计算机上的打印程序只能有一个,这里就可以通过单例模式来避免两个打印任务同时输出到打印机中,即在整个的打印过程中我只有一个打印程序的实例。
1. ARC中的单例实现
+ (instancetype)sharedInstance
{
if (_instance == nil) {
_instance = [[self alloc] init];
}
}
上面代码如果不用GCD或互斥锁会出现以下情况:
假设此时有两条线程:线程1和线程2,都在调用shareInstance方法来创建单例,那么线程1运行到if (_instance == nil)出发现_instance = nil,那么就会初始化一个_instance,假设此时线程2也运行到if的判断处了,此时线程1还没有创建完成实例_instance,所以此时_instance = nil还是成立的,那么线程2又会创建一个_instace。
1.1 GCD方式
#import <Foundation/Foundation.h>
@interface Singleton : NSObject
// 单例方法
+ (instancetype)sharedSingleton;
@end
#import "Singleton.h"
@implementation Singleton
// 全局变量
static id _instance = nil;
// 单例方法
+ (instancetype)sharedSingleton{
return [[self alloc] init];
}
// alloc会调用allocWithZone:
+(instancetype)allocWithZone:(struct _NSZone *)zone{
//只进行一次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [super allocWithZone:zone];
});
return _instance;
}
// 初始化方法
- (instancetype)init{
// 只进行一次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [super init];
});
return _instance;
}
// copy在底层 会调用copyWithZone:
- (id)copyWithZone:(NSZone *)zone{
return _instance;
}
+ (id)copyWithZone:(struct _NSZone *)zone{
return _instance;
}
+ (id)mutableCopyWithZone:(struct _NSZone *)zone{
return _instance;
}
- (id)mutableCopyWithZone:(NSZone *)zone{
return _instance;
}
@end
测试代码:
#import <Foundation/Foundation.h>
#import "Singleton.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Singleton *s1 = [[Singleton alloc]init];
Singleton *s2 = [Singleton sharedSingleton];
Singleton *s3 = [[Singleton alloc]init];
Singleton *s4 = [Singleton sharedSingleton];
Singleton *s5 = [s4 copy];
NSLog(@"%p %p %p %p %p",s1,s2,s3,s4, s5);
}
return 0;
}
dispatch_once保证程序在运行过程中只会被运行一次,那么假设此时线程1先执行shareInstance方法,创建了一个实例对象,线程2就不会再去执行dispatch_once的代码了。从而保证了只会创建一个实例对象。
1.2 互斥锁方式
static id _instance;
+ (instancetype)sharedInstance
{
@synchronized(self) {
if (_instance == nil) {
_instance = [[self alloc] init];
}
}
return _instance;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone
{
@synchronized(self) {
if (_instance == nil) {
_instance = [super allocWithZone:zone];
}
}
return _instance;
}
- (id)copyWithZone:(NSZone *)zone
{
return _instance;
}
被当做一个任务被加上了一把锁。此时假设线程2也想执行shareInstance方法创建单例,但是看到了线程1加的互斥锁,就会进入睡眠模式。等到线程1执行完毕,才会被唤醒,然后去执行上面所示的创建单例的代码,但是此时_instance !=nil,所以不会再创建新的实例对象了。从而保证只会创建一个实例对象。
但是互斥锁会影响性能,所以最好还是使用GCD方式创建单例。
2. 非ARC中实现单例
#import <Foundation/Foundation.h>
@interface Singleton : NSObject
+ (instancetype)sharedSingleton;
@end
#import "Singleton.h"
@implementation Singleton
static id _instance = nil;
+(instancetype)sharedSingleton{
// 系统的大多数类方法都有做autorelease,所以我们也需要做一下
return [[[self alloc] init] autorelease];
}
// alloc会调用allocWithZone:
+(instancetype)allocWithZone:(struct _NSZone *)zone{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [super allocWithZone:zone];
});
return _instance;
}
-(instancetype)init{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [super init];
});
return _instance;
}
- (oneway void)release{
// 什么都不做 保证单例对象不被销毁
}
// 返回本身 保证只有一个单例对象
- (instancetype)retain{
return _instance;
}
// 计数器为1 保证只有一个单例对象
- (NSUInteger)retainCount{
return 1;
}
// copy在底层 会调用copyWithZone:
+ (id)copyWithZone:(struct _NSZone *)zone{
return _instance;
}
- (id)copyWithZone:(NSZone *)zone{
return _instance;
}
+ (id)mutableCopyWithZone:(struct _NSZone *)zone{
return _instance;
}
- (id)mutableCopyWithZone:(NSZone *)zone{
return _instance;
}
@end
测试代码:
#import <Foundation/Foundation.h>
#import "Singleton.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Singleton *s1 = [[Singleton alloc]init];
Singleton *s2 = [Singleton sharedSingleton];
Singleton *s3 = [[Singleton alloc]init];
Singleton *s4 = [Singleton sharedSingleton];
Singleton *s5 = [s4 copy];
NSLog(@"%p %p %p %p %p",s1,s2,s3,s4, s5);
[s1 release];
[s3 release];
[s5 release];
}
return 0;
}
3. 可以将单例模式的实现抽出一个宏
如果我们需要在程序中创建多个单例,那么需要在每个类中都写上一次上述代码,非常繁琐。
我们可以使用宏来封装单例的创建,这样任何类需要创建单例,只需要一行代码就搞定了。
下面以ARC为例:
// .h文件 ##表示拼接字符
#define SingletonH(methodName) +(instancetype)shared##methodName;
// .m文件
#define SingletonM(methodName)\
\
static id _instance = nil;\
+(instancetype)sharedSingleton{\
return [[self alloc] init];\
}\
+(instancetype)allocWithZone:(struct _NSZone *)zone{\
static dispatch_once_t onceToken;\
dispatch_once(&onceToken, ^{\
_instance = [super allocWithZone:zone];\
});\
return _instance;\
}\
- (instancetype)init{\
static dispatch_once_t onceToken;\
dispatch_once(&onceToken, ^{\
_instance = [super init];\
});\
return _instance;\
}\
- (id)copyWithZone:(NSZone *)zone{\
return _instance;\
}\
+ (id)copyWithZone:(struct _NSZone *)zone{\
return _instance;\
}
下面给出可以同时兼容ARC和非ARC的单例实现宏定义:
// .h文件的实现
#define SingletonH(methodName) + (instancetype)shared##methodName;
// .m文件的实现
#if __has_feature(objc_arc) // 是ARC
#define SingletonM(methodName) \
static id _instace = nil; \
+ (id)allocWithZone:(struct _NSZone *)zone \
{ \
if (_instace == nil) { \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instace = [super allocWithZone:zone]; \
}); \
} \
return _instace; \
} \
\
- (id)init \
{ \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instace = [super init]; \
}); \
return _instace; \
} \
\
+ (instancetype)shared##methodName \
{ \
return [[self alloc] init]; \
} \
+ (id)copyWithZone:(struct _NSZone *)zone \
{ \
return _instace; \
} \
\
+ (id)mutableCopyWithZone:(struct _NSZone *)zone \
{ \
return _instace; \
}
#else // 不是ARC
#define SingletonM(methodName) \
static id _instace = nil; \
+ (id)allocWithZone:(struct _NSZone *)zone \
{ \
if (_instace == nil) { \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instace = [super allocWithZone:zone]; \
}); \
} \
return _instace; \
} \
\
- (id)init \
{ \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instace = [super init]; \
}); \
return _instace; \
} \
\
+ (instancetype)shared##methodName \
{ \
return [[self alloc] init]; \
} \
\
- (oneway void)release \
{ \
\
} \
\
- (id)retain \
{ \
return self; \
} \
\
- (NSUInteger)retainCount \
{ \
return 1; \
} \
+ (id)copyWithZone:(struct _NSZone *)zone \
{ \
return _instace; \
} \
\
+ (id)mutableCopyWithZone:(struct _NSZone *)zone \
{ \
return _instace; \
}
#endif
使用方式:
#import <Foundation/Foundation.h>
#import "Singleton.h"
@interface LLDataTool : NSObject
SingletonH(DataTool)
@end
#import "LLDataTool.h"
@implementation LLDataTool
SingletonM(DataTool)
@end
测试代码:
LLDataTool *tool0 = [LLDataTool sharedDataTool];
LLDataTool *tool1 = [LLDataTool sharedDataTool];
LLDataTool *tool2 = [LLDataTool sharedDataTool];
LLDataTool *tool3 = [[LLDataTool alloc] init];
NSLog(@"%p %p %p %p", tool0, tool1, tool2, tool3);
NSLog(@"%@", tool0);
结果截图: