先附上写法,后面分析
+ (instancetype)shareInstance {
return [[self alloc] init];
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
static dispatch_once_t onceToken;
static ObjManager * instance;
dispatch_once(&onceToken, ^{
instance = [super allocWithZone:zone];
});
return instance;
}
0x01单例的调用方式
- 直接通过类方法调用
ObjManager *obj1 = [ObjManager shareInstance];
- 通过普通方式创建
ObjManager *obj2 = [[ObjManager alloc] init]
ObjManager *obj3 = [ObjManager new]
- 通过已经不再使用的NSZone调用
ObjManager *obj4 = [[ObjManager allocWithZone:NULL] init];
目标
创建一个单例必须要满足以上四种调用生成的都只是同一个对象
0x02写法延伸
构建类方法
+ (instancetype)shareInstance {
static dispatch_once_t onceToken;
static ObjManager *instance;
dispatch_once(&onceToken, ^{
instance = [[ObjManager alloc] init];
});
return instance;
}
测试
ObjManager *obj = [[ObjManager alloc] init];
ObjManager *obj1 = [ObjManager shareInstance];
ObjManager *obj2 = [ObjManager new];
ObjManager *obj3 = [[ObjManager allocWithZone:NULL] init];
ObjManager *obj4 = [ObjManager shareInstance];
NSLog(@"\nobj:%@\nobj1:%@\nobj2:%@\nobj3:%@\nobj4:%@",obj, obj1, obj2, obj3, obj4);
# 打印输出
obj:<ObjManager: 0x60000137c480>
obj1:<ObjManager: 0x60000137c4b0>
obj2:<ObjManager: 0x60000137c4c0>
obj3:<ObjManager: 0x60000137c4d0>
obj4:<ObjManager: 0x60000137c4b0>
# 只有obj1和obj4是通过shareInstance创建的一样,其他都不同
只有通过shareInstance调用的方法生成的单例才是一样的,如果通过其他几种方法生成的对象并不是一个单例
将实现向底层迁移
由于单例是共享一块存储空间,所以我们考虑对alloc方法进行重写;
如果重写init是无效的,init只是用来初始化分配好的那块内存空间的。
+ (instancetype)shareInstance {
return [[self alloc] init];
}
+ (instancetype)alloc {
static dispatch_once_t onceToken;
static ObjManager *instance;
dispatch_once(&onceToken, ^{
instance = [super alloc];
});
return instance;
}
测试
ObjManager *obj = [[ObjManager alloc] init];
ObjManager *obj1 = [ObjManager shareInstance];
ObjManager *obj2 = [ObjManager new];
ObjManager *obj3 = [[ObjManager allocWithZone:NULL] init];
ObjManager *obj4 = [ObjManager shareInstance];
NSLog(@"\nobj:%@\nobj1:%@\nobj2:%@\nobj3:%@\nobj4:%@",obj, obj1, obj2, obj3, obj4);
# 打印输出
obj:<ObjManager: 0x600000b1fdc0>
obj1:<ObjManager: 0x600000b1fdc0>
obj2:<ObjManager: 0x600000b1fdc0>
obj3:<ObjManager: 0x600000b1fdd0>
obj4:<ObjManager: 0x600000b1fdc0>
# 除了obj3是通过allocWithZone:,无法涵盖其他都可以
将内存分配再向下迁移
+ (instancetype)shareInstance {
return [[self alloc] init];
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
static dispatch_once_t onceToken;
static ObjManager * instance;
dispatch_once(&onceToken, ^{
instance = [super allocWithZone:zone];
});
return instance;
}
测试
ObjManager *obj = [[ObjManager alloc] init];
ObjManager *obj1 = [ObjManager shareInstance];
ObjManager *obj2 = [ObjManager new];
ObjManager *obj3 = [[ObjManager allocWithZone:NULL] init];
ObjManager *obj4 = [ObjManager shareInstance];
NSLog(@"\nobj:%@\nobj1:%@\nobj2:%@\nobj3:%@\nobj4:%@",obj, obj1, obj2, obj3, obj4);
# 打印输出
obj:<ObjManager: 0x600000630500>
obj1:<ObjManager: 0x600000630500>
obj2:<ObjManager: 0x600000630500>
obj3:<ObjManager: 0x600000630500>
obj4:<ObjManager: 0x600000630500>
# 可以完全确保无论如何构建单例,都不会出现返回不同的对象效果
0x03 执行过程
// 在此处打下断点
ObjManager *obj = [[ObjManager alloc] init];
等到执行此处时,我们设置断点:
# lldb 命令,因为我们没有实现ObjManager的alloc方法,因此需要打NSObject alloc的断点,看执行过程
(lldb) br set -n "+[NSObject alloc]"
Breakpoint 5: where = libobjc.A.dylib`+[NSObject alloc], address = 0x000000010ed07f34
# 继续执行
(lldb) c
进入汇编调试页面
libobjc.A.dylib`+[NSObject alloc]:
-> 0x10ed07f34 <+0>: jmp 0x10ed07f58 ; _objc_rootAlloc
即将开始调用 _objc_rootAlloc,再次设置符号断点
(lldb) br set -n "_objc_rootAlloc"
Breakpoint 6: where = libobjc.A.dylib`_objc_rootAlloc, address = 0x000000010ed07f58
# 执行,进入汇编页面
(lldb) c
进入汇编调试页面
libobjc.A.dylib`_objc_rootAlloc:
-> 0x10ed07f58 <+0>: movq (%rdi), %rax
0x10ed07f5b <+3>: testb $0x40, 0x1d(%rax)
0x10ed07f5f <+7>: je 0x10ed07f66 ; <+14>
0x10ed07f61 <+9>: jmp 0x10ed020a3 ; _objc_rootAllocWithZone
0x10ed07f66 <+14>: movq 0x140eb(%rip), %rsi ; "allocWithZone:"
0x10ed07f6d <+21>: xorl %edx, %edx
0x10ed07f6f <+23>: jmpq *0x120eb(%rip) ; (void *)0x000000010ecee400: objc_msgSend
通过内部实现可以看到最终会进入到allocWithZone:方法
总结:
无论是通过什么方式进行创建,最初想操作系统申请内存空间的方法一定会调用allocWithZone:。
单例的严谨写法可以归为:
+ (instancetype)shareInstance {
return [[self alloc] init];
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
static dispatch_once_t onceToken;
static ObjManager * instance;
dispatch_once(&onceToken, ^{
instance = [super allocWithZone:zone];
});
return instance;
}