先附上寫法漆羔,后面分析
+ (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單例的調(diào)用方式
- 直接通過類方法調(diào)用
ObjManager *obj1 = [ObjManager shareInstance];
- 通過普通方式創(chuàng)建
ObjManager *obj2 = [[ObjManager alloc] init]
ObjManager *obj3 = [ObjManager new]
- 通過已經(jīng)不再使用的NSZone調(diào)用
ObjManager *obj4 = [[ObjManager allocWithZone:NULL] init];
目標(biāo)
創(chuàng)建一個單例必須要滿足以上四種調(diào)用生成的都只是同一個對象
0x02寫法延伸
構(gòu)建類方法
+ (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創(chuàng)建的一樣婆芦,其他都不同
只有通過shareInstance調(diào)用的方法生成的單例才是一樣的,如果通過其他幾種方法生成的對象并不是一個單例
將實現(xiàn)向底層遷移
由于單例是共享一塊存儲空間穆刻,所以我們考慮對alloc方法進(jìn)行重寫置尔;
如果重寫init是無效的,init只是用來初始化分配好的那塊內(nèi)存空間的氢伟。
+ (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:榜轿,無法涵蓋其他都可以
將內(nèi)存分配再向下遷移
+ (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>
# 可以完全確保無論如何構(gòu)建單例幽歼,都不會出現(xiàn)返回不同的對象效果
0x03 執(zhí)行過程
// 在此處打下斷點
ObjManager *obj = [[ObjManager alloc] init];
等到執(zhí)行此處時,我們設(shè)置斷點:
# lldb 命令谬盐,因為我們沒有實現(xiàn)ObjManager的alloc方法甸私,因此需要打NSObject alloc的斷點,看執(zhí)行過程
(lldb) br set -n "+[NSObject alloc]"
Breakpoint 5: where = libobjc.A.dylib`+[NSObject alloc], address = 0x000000010ed07f34
# 繼續(xù)執(zhí)行
(lldb) c
進(jìn)入?yún)R編調(diào)試頁面
libobjc.A.dylib`+[NSObject alloc]:
-> 0x10ed07f34 <+0>: jmp 0x10ed07f58 ; _objc_rootAlloc
即將開始調(diào)用 _objc_rootAlloc飞傀,再次設(shè)置符號斷點
(lldb) br set -n "_objc_rootAlloc"
Breakpoint 6: where = libobjc.A.dylib`_objc_rootAlloc, address = 0x000000010ed07f58
# 執(zhí)行皇型,進(jìn)入?yún)R編頁面
(lldb) c
進(jìn)入?yún)R編調(diào)試頁面
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
通過內(nèi)部實現(xiàn)可以看到最終會進(jìn)入到allocWithZone:方法
總結(jié):
無論是通過什么方式進(jìn)行創(chuàng)建,最初想操作系統(tǒng)申請內(nèi)存空間的方法一定會調(diào)用allocWithZone:砸烦。
單例的嚴(yán)謹(jǐn)寫法可以歸為:
+ (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;
}