iOS 单例的正确写法

单例是 iOS 设计模式之一

1、大家一般都会这么写
#import "Singleton.h"

@implementation Singleton

+ (instancetype)sharedInstance
{
    static Singleton *singleton = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        singleton = [[self alloc] init];
    });
    return singleton;
}

一般情况下,初始化一个类 会使用 init,假如使用 init 创建,是否还是只有一个实例

NSLog(@"1: %p",[Singleton sharedInstance]);
NSLog(@"2: %p",[Singleton sharedInstance]);
NSLog(@"3: %p",[[Singleton alloc] init]);
NSLog(@"4: %p",[[Singleton alloc] init]);

1: 0x600002d5b950
2: 0x600002d5b950
3: 0x600002d642e0
4: 0x600002d60060

1 和 2 是一样的,但是和 3 与 4 都不一样
所以这样写是不完善的

2、改善一下
+ (instancetype)sharedInstance
{
    static Singleton *singleton = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        singleton = [[super allocWithZone:NULL] init];
    });
    return singleton;
}
+ (id)allocWithZone:(struct _NSZone *)zone
{
    return [self sharedInstance];
}

再次打印,得到结果

2019-05-27 01:41:22.327625+0800 test001[44286:3651397] 1: 0x60000242c230
2019-05-27 01:41:22.327686+0800 test001[44286:3651397] 2: 0x60000242c230
2019-05-27 01:41:22.327802+0800 test001[44286:3651397] 3: 0x60000242c230
2019-05-27 01:41:22.327922+0800 test001[44286:3651397] 4: 0x60000242c230

得到的都是同一个对象
使用 init 方法时,每次都会调用 init ,都会执行 init 方法
Singleton init 方法

- (instancetype)init
{
    self = [super init];
    if (self) {
        NSLog(@"init");
    }
    return self;
}
NSLog(@"5: %p",[[Singleton alloc] init]);
NSLog(@"6: %p",[[Singleton alloc] init]);
NSLog(@"7: %p",[[Singleton alloc] init]);
NSLog(@"8: %p",[[Singleton alloc] init]);
// 打印结果
2019-05-27 01:51:40.563060+0800 test001[44465:3684646] init
2019-05-27 01:51:40.563114+0800 test001[44465:3684646] 5: 0x600001cf8e30
2019-05-27 01:51:40.563172+0800 test001[44465:3684646] init
2019-05-27 01:51:40.563223+0800 test001[44465:3684646] 6: 0x600001cf8e30
2019-05-27 01:51:40.563272+0800 test001[44465:3684646] init
2019-05-27 01:51:40.563320+0800 test001[44465:3684646] 7: 0x600001cf8e30
2019-05-27 01:51:40.563440+0800 test001[44465:3684646] init
2019-05-27 01:51:40.563543+0800 test001[44465:3684646] 8: 0x600001cf8e30

每次都会调用 init 方法

- (instancetype)init
{
    if (singleton != nil)
        return self;
    
    self = [super init];
    if (self) {
        NSLog(@"init");
    }
    return self;
}

这样处理后,不管怎样,都只会调用一次 init,每次得到的对象也同一个对象

测试一下

#import "Singleton.h"

static Singleton *singleton = nil;

@implementation Singleton

- (instancetype)init
{
    if (singleton != nil)
        return self;
    
    self = [super init];
    if (self) {
        NSLog(@"init");
    }
    return self;
}
+ (instancetype)sharedInstance
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        singleton = [[super allocWithZone:NULL] init];
    });
    return singleton;
}
+ (id)allocWithZone:(struct _NSZone *)zone
{
    return [self sharedInstance];
}

    // 在其他文件中,使用这个单例
    Singleton *s1 = [Singleton sharedInstance];
    s1.name = @"001";

    Singleton *s2 = [Singleton sharedInstance];
    NSLog(@"A: %@",s2.name);
    s2.name = @"002";

    Singleton *s3 = [[Singleton alloc] init];
    NSLog(@"B: %@",s3.name);
    s3.name = @"003";

    Singleton *s4 = [[Singleton alloc] init];
    NSLog(@"C: %@",s4.name);
// 打印结果
2019-05-27 01:56:31.034039+0800 test001[44526:3700399] init
2019-05-27 01:56:31.034143+0800 test001[44526:3700399] A: 001
2019-05-27 01:56:31.034237+0800 test001[44526:3700399] B: 002
2019-05-27 01:56:31.034436+0800 test001[44526:3700399] C: 003

单例另一种写法
+ (instancetype)sharedInstance
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        singleton = [[Singleton alloc] init];
    });
    return singleton;
}
// 使用 alloc 方法初始化一个类的实例的时候,默认是调用了 allocWithZone 的方法
+ (id)allocWithZone:(struct _NSZone *)zone
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        singleton = [super allocWithZone:zone];
    });
    return singleton;
}
// 测试如下
NSLog(@"\n%p\n%p",[Singleton sharedInstance],[Singleton sharedInstance]);
NSLog(@"\n%p\n%p",[[Singleton alloc] init],[[Singleton alloc] init]);

得到四个指针地址是一样的

你可能感兴趣的:(iOS 单例的正确写法)