美文网首页
Objective-C单例的严谨写法

Objective-C单例的严谨写法

作者: 哦小小树 | 来源:发表于2020-04-22 09:56 被阅读0次
先附上写法,后面分析
+ (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;
}

相关文章

  • Objective-C单例的严谨写法

    先附上写法,后面分析 0x01单例的调用方式 直接通过类方法调用 通过普通方式创建 通过已经不再使用的NSZone...

  • 严谨的单例写法

    把一个类单例化,需要保证在整个进程生命周期内,这个类最多只存在一个实例。有几点需要考虑: 保证多线程下只为此类分配...

  • 单例的严谨写法

    转载自http://www.jianshu.com/p/85618bcd4fee?utm_source=tuico...

  • iOS之手写单例

    一 不严谨写法 先附上不严谨的创建单例的写法 SignalModel.h SignalModel.m 外界使用 打...

  • 单例类的严谨写法

    单例类的简单实现 .h文件 .m文件 这样就创建了一个用户单例类,但是这样做的不严谨处在于,如果用户非要用[[Us...

  • Swift单例模式

    参考:http://swifter.tips/singleton/ OC写法 在 Objective-C 中单例的...

  • 24.单例的严谨写法

  • Objective-C单例写法

  • OC和Swift单例的写法

    一 、OC中单例的写法:1.普通单例的写法 2. 利用多线程来写单例 #pragma mark --- 普通单例写...

  • iOS 单例

    Objective-C 单例宏 Swift 单例声明

网友评论

      本文标题:Objective-C单例的严谨写法

      本文链接:https://www.haomeiwen.com/subject/xddbihtx.html