一、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] <Singleton: 0x6000008e0680>
2020-03-31 16:34:19.548780+0800 OC中的单例[37254:1422270] <Singleton: 0x6000008f4520>
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
网友评论