一、OC 中創(chuàng)建單例的幾種方式
1.1 單線程模式單例
/// 單線程模式單例
+ (instancetype)sharedInstance{
static Singleton *singleton = nil;
if(!singleton){
singleton = [[Singleton alloc] init];
}
return singleton;
}
存在的問題就是: 只能在單線程中使用, 一旦有多線程同時(shí)調(diào)用sharedInstance, 就會(huì)創(chuàng)建多個(gè)singleton對(duì)象, 如下:
- (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(@"全部執(zhí)行完畢.");
});
}
打印結(jié)果:
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] 全部執(zhí)行完畢.
1.2 通過@synchronized加鎖的多線程單例
+ (instancetype)sharedInstance {
static Singleton *singleton;
@synchronized (self) {
if (!singleton) {
singleton = [[Singleton alloc] init];
}
}
return singleton;
}
存在的問題就是: 只有在singleton未創(chuàng)建時(shí), 加鎖才是必要的, 如果singleton已經(jīng)創(chuàng)建, 這個(gè)時(shí)候還加鎖的話,反而會(huì)影響性能.
1.2 GCD多線程方式實(shí)現(xiàn)單例
+ (instancetype)sharedInstance {
static dispatch_once_t onceToken;
static Singleton *singleton;
dispatch_once(&onceToken, ^{
singleton = [[Singleton alloc] init];
});
return singleton;
}
注: dispatch_once 無論使用多線程還是單線程厅克,都只執(zhí)行一次, 在安全的前提下也保證了性能, 是官方推薦的方式.
dispatch_once 主要是根據(jù)onceToken 的值來決定怎么去執(zhí)行代碼赔退。
1.當(dāng)onceToken=0時(shí),線程執(zhí)行dispatch_once的block中代碼
2.當(dāng)onceToken=-1時(shí)证舟,線程跳過dispatch_once的block中代碼不執(zhí)行
3.當(dāng)onceToken為其他值時(shí)硕旗,線程被阻塞,等待onceToken值改變
dispatch_once 執(zhí)行的流程
1.當(dāng)線程調(diào)用shareInstance,此時(shí)onceToken = 0女责,調(diào)用block中的代碼卵渴。 此時(shí)onceToken的值變?yōu)?40734537148864。
2.當(dāng)其他線程再調(diào)用shareInstance方法時(shí)鲤竹,onceToken的值已經(jīng)是140734537148864了浪读,線程阻塞。
3.當(dāng)block線程執(zhí)行完block之后辛藻,onceToken變?yōu)?1.其他線程不再阻塞碘橘,跳過block。
4.下次再調(diào)用shareInstance時(shí)吱肌,block已經(jīng)為-1.直接跳過block痘拆。
1.3 團(tuán)隊(duì)開發(fā)情況下的單例的封裝
當(dāng)團(tuán)隊(duì)進(jìn)行開發(fā)的時(shí)候, 創(chuàng)建對(duì)象的方法也可能就各不相同, 但是為了保證對(duì)象的唯一性, 需要:
// 當(dāng)static 關(guān)鍵字修飾局部變量時(shí),只會(huì)初始化一次且在程序中只有一份內(nèi)存
static Singleton* _singleton = nil;
+ (instancetype)sharedInstance {
static dispatch_once_t onceToken ;
dispatch_once(&onceToken, ^{
_singleton = [[super allocWithZone:NULL] init] ;
/**
1. 不使用alloc 方法氮墨,而是調(diào)用[[super allocWithZone:NULL] init]
2. 已經(jīng)重載allocWithZone 基本的對(duì)象分配方法纺蛆,所以要借用父類(NSObject)的功能來幫助出處理底層內(nèi)存分配
3. 如果_singleton = [self alloc] init];創(chuàng)建的話,將會(huì)和allocWithZone產(chǎn)生死鎖规揪。dispatch_once中的onceToken線程被阻塞桥氏,等待onceToken值改變。
*/
}) ;
return _singleton ;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone{
return [Singleton sharedInstance];
}
// 須遵守 NSCopying 協(xié)議
- (id)copyWithZone:(NSZone *)zone{
return [Singleton sharedInstance];
}
// 須遵守 NSMutableCopying 協(xié)議
- (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:靜態(tài)的 let: 常量 -> 全局訪問點(diǎn)
static let shared: Singleton = Singleton()
}
2.1 Swift 中的單例寫法二
import UIKit
class Singleton: NSObject {
static let shared2: Singleton = {
let singleton = Singleton()
// 可以做一些 初始化字支、自定義的操作
return singleton
}()
}
.End