就是保证某个类建立出来的对象从始到终只有一个的一种方案git
首先将咱们的环境设置为非ARC环境,即MRC,如图 github
在MRC模式下,咱们得本身手动释放资源,因此得重写一些与资源建立与释放相关的方法,以保证单例对象的惟一。设计模式
新建一个继承于NSObject的类 LXFFileTool,我直接上代码,并写上注释 LXFFileTool.h安全
@interface LXFFileTool : NSObject
+ (instancetype)sharedFileTool;
@end
复制代码
LXFFileTool.mbash
#import "LXFFileTool.h"
@implementation LXFFileTool
static LXFFileTool *_fileTools = nil;
/** * alloc方法内部会调用allocWithZone: * @param zone 系统分配给app的内存 */
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
if (_fileTools == nil) {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{ // 安全(这个代码只会被调用一次)
_fileTools = [super allocWithZone:zone];
});
}
return _fileTools;
}
- (oneway void)release {
// 在allocWithZone中使用了GCD令建立对象的代码只执行一次,若是_fileTools被释放则没法再建立
// 重写release方法,防止_fileTools被释放
}
// 重写retain方法
- (instancetype)retain {
return self;
}
// 重写retainCount锁定引用计数
- (NSUInteger)retainCount {
return 1;
}
// 重写init方法,防止单例所拥有的属性值被重置
// 让初始化的方法只能执行一次,天然属性值就没有机会被重置
- (instancetype)init {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_fileTools = [super init]; // init会先调用alloc方法
});
return _fileTools;
}
// 仿造系统的单例建立方式,提供类方法
+ (instancetype)sharedFileTool {
// 因为咱们已经重写了init方法保证了单例对象的惟一了,因此这里直接调用init方法便可。
return [[self alloc] init];
}
@end
复制代码
MRC下就是这样,咱们的目的就是只能建立和初始化一次对象,不给机会释放,也不给机会从新初始化,从而保证了该对象的惟一。微信
那如今来看看ARC下是如何实现单例的吧。其实ARC下与MRC的区别就是ARC下咱们不用本身再手动去释放资源了,从而使代码上大同小异,以下所示。app
#import "LXFFileTool.h"
@implementation LXFFileTool
static LXFFileTool *_fileTools = nil;
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
if (_fileTools == nil) {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_fileTools = [super allocWithZone:zone];
});
}
return _fileTools;
}
- (instancetype)init {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_fileTools = [super init];
});
return _fileTools;
}
+ (instancetype)sharedFileTool {
return [[self alloc] init];
}
复制代码
如今咱们已经知道了ARC与MRC下分别是如何建立单例的了,可是若是咱们一个项目里须要多个单例,那咱们只能把代码复制粘贴再改改就完事吗?这未免也太麻烦了吧。那咱们能不能作到快速且方便的建立单例对象呢?能够的,利用宏ui
首先先说下一些关于宏的知识吧spa
那如今来讨论下一些疑惑吧,你说宏只能单行,但是建立单例的代码但是有不少行呀!还有咱们如何作到自定义类方法名(就是 sharedXXX )?好,咱们来介绍下宏下的两个特殊符号设计
做用 | |
---|---|
\ | 用来转译换行符,即屏蔽换行符 |
## | 将两个相邻的标记(token)链接为一个单独的标记 |
想了解其它关于宏的预处理命令能够自行百度参考"C语言的预处理命令"
简单来讲,\用于取消换行,##用来链接,而咱们就用##来实现自定义类方法名
建立一个头文件Singleton.h用来存放宏定义 先来看看定义.h中 sharedXXX 是如何经过宏来定义的
// .h文件的实现
#define SingletonH(methodName) + (instancetype)shared##methodName;
复制代码
如今回到LXFFileTool.h中,直接一行定义sharedFileTool这个类方法
#import "Singleton.h"
@interface LXFFileTool : NSObject
SingletonH(FileTool)
@end
复制代码
咱们只须要将方法名FileTool传入SingletonH()中就能够拼接为sharedFileTool
那如今再来看看定义.m中建立单例的方式,以ARC为例
#define SingletonM(methodName) \
static id _instance = nil; \
+ (instancetype)allocWithZone:(struct _NSZone *)zAone { \
if (_instance == nil) { \
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; \
} \
\
+ (instancetype)shared##methodName { \
return [[self alloc] init]; \
}
复制代码
在每个行后面加上(反斜杠)取消换行,使用##来拼接传入的方法名,但还有一点须要注意:最后一行不能加反斜杠
回到LXFFileTool.m中,一行实现建立单例
#import "LXFFileTool.h"
@implementation LXFFileTool
SingletonM(FileTool)
@end
复制代码
好,如今还有一个问题,就是若是个人项目中有个别文件是须要MRC环境的,那我该怎么办才能让建立单例也是如此简单呢?很简单,加个判断就行了,大体判断以下,详情看文章最后附上的Demo
#if __has_feature(objc_arc) // ARC
// 写上ARC下的定义代码
#else // 非ARC
// 写上MRC下的定义代码
#endif
复制代码
好了,如今用起来是否是方便多了?咱们只要建立一个类,而后在.h文件中写SingletonH(XXX),再在.m文件中写SingletonM(XXX)就能够实现单例了~
顺便提下如何在MRC下指定某个类文件使用的环境为ARC
-fobjc-arc
复制代码
若是是指定MRC,则写上
-fno-objc-arc
复制代码
最后,附上Demo: LXFSingleton