什么单例
单例是指整个程序中有且只有一个对象,具有全局唯一性,单例必须要自行创建一个实例,单例必须要始终提供一个全局实例暴露给外部使用。
为什么用单例模式
创建并使用一个单例,就是引入了单例模式;使用单例模式,是为了避免在整个程序中创建多个对象。
创建一个单例条件
- 提供一个静态实例,一般设置成nil
- 提供一个创建单例实例的方法,如果实例存在就返回实例,否则创建一个实例
- 在OC中,需要重写
+ (instancetype)allocWithZone:(struct _NSZone *)zone
方法,因为这个方法会在alloc
、new
等方法调用后调用
allocWithZone:(struct _NSZone *)zone
与 alloc
实际上alloc内部会调用allocWithZone,也就是说allocWithZone方法更底层。也就是说我们实现alloc方法的话就只能拦截alloc方法,但是实现allocWithZone方法的话,任何内存分配的方法我们都能拦截。
NSZone
是用来分配和管理一段内存空间的结构体,查找NSZone
的头文件能够发现以下函数
FOUNDATION_EXPORT NSZone *NSCreateZone(NSUInteger startSize, NSUInteger granularity, BOOL canFree) NS_SWIFT_UNAVAILABLE("Zone-based memory management is unavailable");
FOUNDATION_EXPORT NSString *NSZoneName(NSZone * __nullable zone) NS_SWIFT_UNAVAILABLE("Zone-based memory management is unavailable");
FOUNDATION_EXPORT NSZone * __nullable NSZoneFromPointer(void *ptr) NS_SWIFT_UNAVAILABLE("Zone-based memory management is unavailable");
FOUNDATION_EXPORT void *NSZoneMalloc(NSZone * __nullable zone, NSUInteger size) NS_SWIFT_UNAVAILABLE("Zone-based memory management is unavailable");
FOUNDATION_EXPORT void *NSZoneCalloc(NSZone * __nullable zone, NSUInteger numElems, NSUInteger byteSize) NS_SWIFT_UNAVAILABLE("Zone-based memory management is unavailable");
FOUNDATION_EXPORT void *NSZoneRealloc(NSZone * __nullable zone, void * __nullable ptr, NSUInteger size) NS_SWIFT_UNAVAILABLE("Zone-based memory management is unavailable");
FOUNDATION_EXPORT void NSZoneFree(NSZone * __nullable zone, void *ptr) NS_SWIFT_UNAVAILABLE("Zone-based memory management is unavailable");
看起来跟C语言的malloc
,calloc
,free
等函数相似,所以大概NSZone就是可以分配一段连续空间。你可以将你创建的类实例都存在这个空间,自己管理。
比如你都用allocWithZone
创建三个NSDictionary
,那这三个字典肯定是连在一起存储的。然后你连着读取三字典的数据,地址相邻理论上读取速度更快。
反正当年肯定用这个有好处,现在苹果都帮你处理好了,不用管了,官方也不建议再用这个接口了。
总结下来就是:
alloc
和allocWithZone
都是创建一个类实例的方法
不同之处在于,alloc
无法指定一个NSZone
来存储自己创建的实例,它最终调用的是allocWithZone(nil)
,使用的是系统给定的NSZone
而allocWithZone
可以指定自己的NSZone来存储自己创建的实例,如果zone
传nil
.它使用的就是系统给定的NSZone
单例的实现
1. 非线程安全
.h
文件,下面的几种方法该文件都是提供相同的方法,不在添加
#import <Foundation/Foundation.h>
@interface XYHSingleton01 : NSObject
+ (instancetype)shareInstance;
@end
.m
文件
#import "XYHSingleton01.h"
@implementation XYHSingleton01
//标准的单利模式 (确定没有多线程的情况 可以使用)
static XYHSingleton01 *_instance = nil;
+ (instancetype)shareInstance {
if (_instance == nil) {
_instance = [[XYHSingleton01 alloc] init];
}
return _instance;
}
//当我调用alloc的时候 回调改方法
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
if (_instance == nil) {
_instance = [super allocWithZone:zone];
}
return _instance;
}
@end
之所以说是非线程安全,是因为,若在多线的情况下,假设线程A和线程B中都在获取这个_instance
,此时,A执行_instance = [[XYHSingleton01 alloc] init];
方法时,还没有执行结束的时候,B又调用shareInstance
方法,但是此时_instance
还没有创建成功,所以B又会去alloc
一个新的对象,从而不满足单例模式。
这种方法,可以应用在确保没有多线程调用的情况下使用,可以提升程序性能,因为加线程保护是性能消耗的。
2. 线程安全(dispatch_once)
.m
文件
#import "XYHSingleton_02.h"
@implementation XYHSingleton_02
static XYHSingleton_02 *_instance = nil;
+ (instancetype)shareInstance {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[XYHSingleton_02 alloc] init];
});
return _instance;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
if (!_instance) {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [super allocWithZone:zone];
});
}
return _instance;
}
@end
dispatch_once
函数式保证在应用程序执行中只执行一次指令处理的API,通过dispatch_once
函数,能够保证实例即使在多线程的情况下,也只初始化一次。
3. @synchronized
关键字
.m
文件
#import "XYHSingleton_04.h"
@implementation XYHSingleton_04
static XYHSingleton_04 *_instance = nil;
+ (instancetype)shareInstance {
@synchronized (self) {
_instance = [[XYHSingleton_04 alloc]init];
}
return _instance;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
if (!_instance) {
@synchronized (self) {
_instance = [super allocWithZone:zone];
}
}
return _instance;
}
@end
@synchronized
结构所做的事情跟锁(lock
)类似:它防止不同的线程同时执行同一段代码。但在某些情况下,相比于使用 NSLock 创建锁对象、加锁和解锁来说,@synchronized
用着更方便,可读性更高。
4. 使用load
方法
.m
文件
#import "XYHSingleton_03.h"
@implementation XYHSingleton_03
static XYHSingleton_03 *_instance = nil;
+ (void)load {
[XYHSingleton_03 shareInstance];
}
+ (instancetype)shareInstance {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[XYHSingleton_03 alloc] init];
});
return _instance;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
if (!_instance) {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [super allocWithZone:zone];
});
}
return _instance;
}
@end
+ load
作为 Objective-C 中的一个方法,与其它方法有很大的不同。它只是一个在整个文件被加载到运行时,在 main
函数调用之前被 ObjC
运行时调用的钩子方法。所以将实例的初始化放在load
方法中,即可在程序启动后就创建了一个_instance
,这个之前懒加载模式(什么时候用什么时候去创建)有点区别。