ios多線程操作— GCD延遲操作和相關(guān)使用方法
0x01.iOS版本
使用GCD函數(shù)可以進(jìn)行延時(shí)操作砾淌,該函數(shù)為
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
});
現(xiàn)在我們來(lái)分解一下參數(shù)
dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)) : NSEC_PER_SEC
在頭文件中的定義如下:
#define NSEC_PER_SEC 1000000000ull /* nanoseconds per second */
該參數(shù)表示從現(xiàn)在開始經(jīng)過(guò)多少納秒
dispatch_get_main_queue():
表示主隊(duì)列. ^{ }:
表示一個(gè)block任務(wù)搜骡。
我們可以來(lái)測(cè)試一下經(jīng)過(guò)多少納秒之后掂榔,由主隊(duì)列調(diào)度任務(wù)是異步執(zhí)行還是同步執(zhí)行,代碼如下:
// when 時(shí)間 從現(xiàn)在開始經(jīng)過(guò)多少納秒
dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC));
void (^task)() = ^ {
// 延遲操作執(zhí)行的代碼
NSLog(@"%@", [NSThread currentThread]);
};
// 經(jīng)過(guò)多少納秒药磺,由主隊(duì)列調(diào)度任務(wù)異步執(zhí)行
dispatch_after(when, dispatch_get_main_queue(), task);
// 先執(zhí)行就是異步,后執(zhí)行就是同步
NSLog(@"come here");
由此可見主隊(duì)列中調(diào)度任務(wù)是異步執(zhí)行的 再將執(zhí)行隊(duì)列改為全局隊(duì)列和串行隊(duì)列,得到的結(jié)果完全是一樣的午绳,由此可知該函數(shù)執(zhí)行的是異步操作。
GCD中有個(gè)函數(shù)能夠保證某段代碼在程序運(yùn)行過(guò)程中只被執(zhí)行1次映之!該函數(shù)如下:
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
})
dispatch_once_t
在頭文件中得定義如下:typedef long dispatch_once_t
; 由此可知該類型是個(gè)long
類型拦焚。當(dāng)onceToken
等于0時(shí)就會(huì)執(zhí)行block
代碼。dispatch_once
是線程安全的杠输,只要涉及到線程安全就會(huì)涉及到鎖赎败,dispatch_once
內(nèi)部也有一把鎖,性能比互斥鎖高蠢甲! 利用該函數(shù)我們可以來(lái)寫一個(gè)單例模式 單例模式可以保證在程序運(yùn)行過(guò)程僵刮,一個(gè)類只有一個(gè)實(shí)例且該實(shí)例易于供外界訪問(wèn),從而方便控制實(shí)例個(gè)數(shù),并節(jié)約系統(tǒng)資源妓笙,當(dāng)應(yīng)用程序需要共享一份資源時(shí)就可以用單例模式來(lái)實(shí)現(xiàn)若河。單例模式分ARC與MRC兩種情況,我們可以用宏判斷是否為ARC環(huán)境
#if __has_feature(objc_arc)
// ARC
#else
// MRC
#endif
ARC環(huán)境下簡(jiǎn)單地單例模式:
@implementation SoundTools
// 定義一個(gè)靜態(tài)成員寞宫,保存唯一的實(shí)例
static id instance;
// 保證對(duì)象只被分配一次內(nèi)存空間萧福,通過(guò)dispatch_once能夠保證單例的分配和初始化是線程安全的
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [super allocWithZone:zone];
});
return instance;
}
// 保證對(duì)象只被初始化一次
+ (instancetype)sharedSoundTools {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}
- (id)copyWithZone:(NSZone *)zone {
return instance;
}
@end
測(cè)試代碼如下:
- (void)viewDidLoad {
[super viewDidLoad];
SoundTools *s1 = [SoundTools sharedSoundTools];
NSLog(@"%p", s1);
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
SoundTools *s2 = [SoundTools sharedSoundTools];
NSLog(@"%p", s2);
}
兩個(gè)方法打印出來(lái)的地址完全一樣!
在MRC環(huán)境下有如下代碼:
// 定義一個(gè)靜態(tài)成員辈赋,保存唯一的實(shí)例
static id instance;
// 保證對(duì)象只被分配一次內(nèi)存空間鲫忍,通過(guò)dispatch_once能夠保證單例的分配和初始化是線程安全的
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [super allocWithZone:zone];
});
return instance;
}
// 保證對(duì)象只被初始化一次
+ (instancetype)sharedSoundTools {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}
- (id)copyWithZone:(NSZone *)zone {
return instance;
}
#pragma mark - MRC內(nèi)存管理方法
/**
因?yàn)閱卫膶?duì)象是保存在靜態(tài)區(qū)的,因此需要重寫 內(nèi)存管理方法钥屈,取消默認(rèn)的引用計(jì)數(shù)操作悟民!
*/
// 默認(rèn)會(huì)將引用計(jì)數(shù)-1
- (oneway void)release {
// 什么也不做,跟highlight類似
}
// 默認(rèn)引用計(jì)數(shù)+1篷就,同時(shí)返回一個(gè)對(duì)象
- (instancetype)retain {
return instance;
}
// 默認(rèn)添加自動(dòng)釋放標(biāo)記射亏,延遲釋放!
- (instancetype)autorelease {
return instance;
}
// 返回有多少個(gè)對(duì)象對(duì)當(dāng)前對(duì)象引用的數(shù)值
- (NSUInteger)retainCount {
// 出處:limits.h 會(huì)根據(jù)CPU的架構(gòu)自行調(diào)整整數(shù)的長(zhǎng)度
return ULONG_MAX;
}
0x02.swift 3.0版本
1.延遲執(zhí)行:
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()+3.0, execute: {
[unowned self] () -> Void in
//延遲操作
})
PS. DispatchTime
對(duì)象用now()
獲取當(dāng)前時(shí)間竭业,加上秒數(shù)即可
2.全局隊(duì)列執(zhí)行耗時(shí)操作后切換到主線程刷新UI
DispatchQueue.global().async {
// 耗時(shí)操作
DispatchQueue.main.async {
// 主線程刷新UI
}
}
3.同步執(zhí)行操作
DispatchQueue.global().sync {
// 同步執(zhí)行
}
4.創(chuàng)建隊(duì)列
DispatchQueue
的默認(rèn)初始化方法創(chuàng)建的是同步隊(duì)列智润,如果要?jiǎng)?chuàng)建并發(fā)的隊(duì)列,在attributes中聲明.concurrent未辆。
// 同步隊(duì)列
let serialQueue = DispatchQueue(label: "name")
// 并發(fā)隊(duì)列
let concurrentQueue = DispatchQueue(label: "name", attributes: .concurrent)
5.執(zhí)行多個(gè)任務(wù)后再做某種操作
使用DispatchGroup
窟绷,所有操作都完成后執(zhí)行notify
。
let group = DispatchGroup()
let queue1 = DispatchQueue(label: "queue1")
queue1.async(group: group) {
// 執(zhí)行任務(wù)1
}
let queue2 = DispatchQueue(label: "queue2")
queue1.async(group: group) {
// 執(zhí)行任務(wù)2
}
group.notify(queue: DispatchQueue.main) {
// 執(zhí)行完成
}
如果要在某一任務(wù)或某幾個(gè)任務(wù)后后執(zhí)行其他任務(wù)咐柜,可在任務(wù)間加上等待:
//等待組內(nèi)任務(wù)全部完成
group.wait(timeout: DispatchTime.distantFuture)
6.DispatchWorkItem
的使用
- DispatchWorkItem可理解為任務(wù)條目兼蜈,可初始化傳入優(yōu)先級(jí)等參數(shù),因其有默認(rèn)值拙友,也可只傳入一個(gè)閉包为狸。同樣,它也有wait方法献宫,使用和上面差不多钥平。
let queue = DispatchQueue(label: "queue", attributes: .concurrent)
let workItem = DispatchWorkItem {
// 任務(wù)
}
queue.async(execute: workItem)
print("before waiting")
workItem.wait()
print("after waiting")
7.barrier
柵欄
- barrier的加入會(huì)等到在它加入隊(duì)列之前的“任務(wù)”執(zhí)行完畢后实撒,才開始執(zhí)行姊途。在它之后加入隊(duì)列的“任務(wù)”,則等到這個(gè)“任務(wù)”執(zhí)行完畢后才開始執(zhí)行知态。這里的“任務(wù)”用DispatchWorkItem創(chuàng)建捷兰。
let barrierWorkItem = DispatchWorkItem(flags: .barrier) {
// 柵欄操作,比如之前有若干“讀”操作负敏,這里有“寫”操作
}
let queue = DispatchQueue(label: "queue", attributes: .concurrent)
queue.async(execute: barrierWorkItem)
8.信號(hào)量
為了線程安全的統(tǒng)計(jì)數(shù)量贡茅,會(huì)使用信號(hào)量作計(jì)數(shù)。初始化方法只有一個(gè),傳入一個(gè)Int類型的數(shù)顶考。
let semaphore = DispatchSemaphore(value: 10)
// 信號(hào)量減一
semaphore.wait()
// 信號(hào)量加一
semaphore.signal()