单例模式的介绍和ARC下的单例请见这篇:【iOS】单例模式
目录
关闭ARC环境
首先关闭ARC环境,即打开MRC:
或是指定某特定目标文件为非ARC环境:
双击某个类文件,指定为ARC环境,输入-fobjc-arc
,指定为MRC环境,输入-fno-objc-arc
。
MRC下的单例
MRC下,我们就需要手动释放资源,通过重写一些与资源创建与释放相关的方法,以保证单例对象的唯一。
SingletonByMRC.h
objectivec
@interface SingletonByMRC : NSObject
+ (instancetype)sharedSingletonByMRC;
@end
SingletonByMRC.m
objectivec
@implementation SingletonByMRC
static SingletonByMRC* instanceVariable = nil;
/*
alloc方法内部会调用allocWithZone:
参数zone时系统分配给App的内存
*/
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
if (!instanceVariable) {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{ // 安全(这段代码只会被调用一次)
instanceVariable = [super allocWithZone: zone];
});
}
return instanceVariable;
}
- (oneway void)release {
// allocWithZone中使用了GCD命令创建对象的代码只执行一次(单例),如果被释放则无法再创建
// 重写release方法,替换为空操作,防止instanceVariable被释放
}
// 重写retain方法
- (instancetype)retain {
return self;
}
// 重写retainCount锁定引用计数
- (NSUInteger)retainCount {
return 1;
}
// 重写init方法,防止单例所拥有的属性值被重置
// 让初始化的方法只能执行一次,自然属性值就没有机会被重置
- (instancetype)init {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instanceVariable = [super init];
});
return instanceVariable;
}
// 仿造系统的单例创建方式,提供类方法
+ (nonnull instancetype)sharedSingletonByMRC {
// 由于我们已经重写了init方法保证了单例对象的唯一了,所以这里直接调用init方法即可。
return [[self alloc] init];
}
@end
ARC下的单例
与MRC的主要区别就是不用再手动去释放资源了。
SingletonByARC.m
objectivec
#import "SingletonByARC.h"
@implementation SingletonByARC
static SingletonByARC* insVar = nil;
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
if (!insVar) {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
insVar = [super allocWithZone: zone];
});
}
return insVar;
}
- (instancetype)init {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
insVar = [super init];
});
return insVar;
}
+ (instancetype)sharedInstance {
return [[self alloc] init];
}
@end
批量创建单例
如果一个项目中需要多个单例,总不能把上面的代码一个一个复制粘贴再改改就完事了吧?那未免也太麻烦了。
我们可以利用宏快速且简单地创建单例。
首先先说下一些关于宏的知识:
- 使用
#define
关键字来定义宏 - 宏定义只能是单行的,不能换行
那现在来讨论下一些疑惑吧,你说宏只能单行,可是创建单例的代码可是有很多行呀!还有我们如何做到自定义类方法名(就是 sharedXXX )?我们来介绍下宏下的两个特殊符号:
符号 | 作用 |
---|---|
\ |
用来转译换行符,即屏蔽换行符 |
## |
将两个相邻的标记(token)连接为一个单独的标记 |
简言之,就是\
用于取消换行,##
用于连接。
创建头文件Singleton.h
存放头文件:
objectivec
#define SingletonH(methodName) + (instancetype)shared##methodName;
#define SingletonM(methodName) \
static id _instance = nil; \
+ (instancetype)allocWithZone:(struct _NSZone *)zone { \
if (!_instance) { \
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]; \
}
SingletonH(methodName)
为声明宏,SingletonM(methodName)
为实现宏。在每一行后面加上(反斜杠)取消换行,使用##来拼接传入的方法名,需要注意的是:最后一行不能加反斜杠。
在SingletonClass.h 文件中直接声明shareSingleMethod
方法:
objectivec
#import "Singleton.h"
@interface SingletonClass : NSObject
SingletonH(SingleMethod);
@end
将方法名SingleMethod
传入SingletonH();中就可以拼接为shareSingleMethod
。
在SingletonClass.m直接实现创建单例类
objectivec
#import "SingletonClass.h"
@implementation SingletonClass
SingletonM(SingleMethod);
@end
运行结果:
最终我们仅仅用了"两"行代码(SingletonH();
、SingletonM();
),成功创建出了一个单例类,这样是不是节省了大量多余冗杂的代码呢。