iOS-单例的正确写法

一、OC 中创建单例的几种方式

1.1 单线程模式单例

/// 单线程模式单例
+ (instancetype)sharedInstance{
    
    static Singleton *singleton = nil;
    if(!singleton){
        
        singleton = [[Singleton alloc] init];
    }
    return singleton;
}

存在的问题就是: 只能在单线程中使用, 一旦有多线程同时调用sharedInstance, 就会创建多个singleton对象, 如下:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    dispatch_queue_t dispatchQueue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_t dispatchGroup = dispatch_group_create();
    dispatch_group_async(dispatchGroup, dispatchQueue, ^(){
        Singleton *s1 = [Singleton sharedInstance];
        NSLog(@"%@",s1);
    });
    dispatch_group_async(dispatchGroup, dispatchQueue, ^(){
        Singleton *s2 = [Singleton sharedInstance];
        NSLog(@"%@",s2);
    });
    dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^(){
        NSLog(@"全部执行完毕.");
    });
}

打印结果:
2020-03-31 16:34:19.548777+0800 OC中的单例[37254:1422273]
2020-03-31 16:34:19.548780+0800 OC中的单例[37254:1422270]
2020-03-31 16:34:19.562469+0800 OC中的单例[37254:1422085] 全部执行完毕.

1.2 通过@synchronized加锁的多线程单例

+ (instancetype)sharedInstance {
    
    static Singleton *singleton;
    @synchronized (self) {
        
        if (!singleton) {
            singleton = [[Singleton alloc] init];
        }
    }
    return singleton;
}

存在的问题就是: 只有在singleton未创建时, 加锁才是必要的, 如果singleton已经创建, 这个时候还加锁的话,反而会影响性能.

1.2 GCD多线程方式实现单例

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

注: dispatch_once 无论使用多线程还是单线程,都只执行一次, 在安全的前提下也保证了性能, 是官方推荐的方式.

dispatch_once 主要是根据onceToken 的值来决定怎么去执行代码。
1.当onceToken=0时,线程执行dispatch_once的block中代码
2.当onceToken=-1时,线程跳过dispatch_once的block中代码不执行
3.当onceToken为其他值时,线程被阻塞,等待onceToken值改变

dispatch_once 执行的流程
1.当线程调用shareInstance,此时onceToken = 0,调用block中的代码。 此时onceToken的值变为140734537148864。
2.当其他线程再调用shareInstance方法时,onceToken的值已经是140734537148864了,线程阻塞。
3.当block线程执行完block之后,onceToken变为-1.其他线程不再阻塞,跳过block。
4.下次再调用shareInstance时,block已经为-1.直接跳过block。

1.3 团队开发情况下的单例的封装

当团队进行开发的时候, 创建对象的方法也可能就各不相同, 但是为了保证对象的唯一性, 需要:

// 当static 关键字修饰局部变量时,只会初始化一次且在程序中只有一份内存
static Singleton* _singleton = nil;

+ (instancetype)sharedInstance {
    
    static dispatch_once_t onceToken ;
    dispatch_once(&onceToken, ^{
        
        _singleton = [[super allocWithZone:NULL] init] ;
/**
 1. 不使用alloc 方法,而是调用[[super allocWithZone:NULL] init]
 2. 已经重载allocWithZone 基本的对象分配方法,所以要借用父类(NSObject)的功能来帮助出处理底层内存分配
 3. 如果_singleton = [self alloc] init];创建的话,将会和allocWithZone产生死锁。dispatch_once中的onceToken线程被阻塞,等待onceToken值改变。
 */
    }) ;
    return _singleton ;
}

+ (instancetype)allocWithZone:(struct _NSZone *)zone{
    return [Singleton sharedInstance];
}

// 须遵守 NSCopying 协议
- (id)copyWithZone:(NSZone *)zone{
    return [Singleton sharedInstance];
}

// 须遵守 NSMutableCopying 协议
- (id)mutableCopyWithZone:(NSZone *)zone{
    return [Singleton sharedInstance];
}

二、Swift 中的单例

格式: Swift 中单例的格式和 Swift中懒加载的方式类似
1.懒加载格式:
- lazy var 名称: 类型 = 类型()
- lazy var 名称: 类型 = { return 类型() }()
2.单例格式
- static let xxx: 类型 = 类型()
- static let xxx: 类型 = { return 类型() }()

2.1 Swift 中的单例写法一

import UIKit

class Singleton: NSObject {

    // static:静态的 let: 常量 -> 全局访问点
    static let shared: Singleton = Singleton()
}

2.1 Swift 中的单例写法二

import UIKit

class Singleton: NSObject {

    static let shared2: Singleton = {
        let singleton = Singleton()
        // 可以做一些 初始化、自定义的操作
        return singleton
    }()
}

.End

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