等待多個(gè)并發(fā)請(qǐng)求同步回調(diào)
例如同時(shí)發(fā)起網(wǎng)絡(luò)請(qǐng)求A,網(wǎng)絡(luò)請(qǐng)求B犯助,網(wǎng)絡(luò)請(qǐng)求C蜂桶,需等待A,B也切,C都返回了才進(jìn)行回調(diào)扑媚。曾經(jīng)是利用 block
嵌套腰湾,就是A回調(diào)嵌B,B回調(diào)嵌C疆股,十分膚淺的做法╮(╯▽╰)╭
這里介紹 GCD
中的兩種方法费坊,第一種是利用 DispatchGroup
的 enter
和 leave
,第二種是利用 DispatchSemaphore
信號(hào)量旬痹。
以下代碼是基于 swift 3.0.2附井。
首先先創(chuàng)建一個(gè)模擬請(qǐng)求。
// 假裝是網(wǎng)絡(luò)請(qǐng)求
func networkRequest(sleepTime: Int, closure: @escaping ()->Void) -> Void {
DispatchQueue.global().async {
Thread.sleep(forTimeInterval: TimeInterval(sleepTime))
// 假裝是成功回調(diào)
closure()
}
}
第一種方法利用 enter&leave
两残。需要注意的是 enter&leave
必須成對(duì)出現(xiàn)永毅,enter
少了會(huì)崩潰,leave
少了則永遠(yuǎn)不會(huì)執(zhí)行 notify
函數(shù)人弓。
這種方法的原理就是:
- 請(qǐng)求前
enter
沼死。 - 請(qǐng)求完成后
leave
。
// 利用 enter/leave 來(lái)控制
func gcd_group_enter_leave() {
let group = DispatchGroup.init()
let queue = DispatchQueue.global()
queue.async(group: group) {
group.enter()
print("1 start")
self.networkRequest(sleepTime:1, closure: {
print("1 end")
group.leave()
})
}
queue.async(group: group) {
group.enter()
print("2 start")
self.networkRequest(sleepTime:2, closure: {
print("2 end")
group.leave()
})
}
queue.async(group: group) {
group.enter()
print("3 start")
self.networkRequest(sleepTime:2, closure: {
print("3 end")
group.leave()
})
}
queue.async(group: group) {
group.enter()
print("4 start")
self.networkRequest(sleepTime:2, closure: {
print("4 end")
group.leave()
})
}
group.notify(queue: queue) { // 所有組完成后回調(diào)
print("all done")
}
}
第二種方法是利用信號(hào)量的 wait&signal
崔赌,簡(jiǎn)單來(lái)說(shuō)就是:signal
就是釋放信號(hào)即信號(hào)量 +1意蛀,wait
就是等待信號(hào)即信號(hào)量-1。wait&signal
也是必須成對(duì)出現(xiàn)健芭。
這種方法的原理就是:
- 創(chuàng)建0信號(hào)量县钥。
- 請(qǐng)求完成釋放信號(hào),使得信號(hào)量+1慈迈。
-
notify
的回調(diào)操作前加入wait
操作(多少請(qǐng)求就加多少wait
若贮,這要做為了得到足夠的信號(hào)量才能執(zhí)行wait
下面的代碼)。
// 利用 semaphore 來(lái)控制
func gcd_semaphore_wait_signal() {
let semaphore = DispatchSemaphore.init(value: 0)
let group = DispatchGroup.init()
let queue = DispatchQueue.global()
queue.async(group: group) {
self.networkRequest(sleepTime:1, closure: {
print("1")
semaphore.signal()
})
}
queue.async(group: group) {
self.networkRequest(sleepTime:2, closure: {
print("2")
semaphore.signal()
})
}
queue.async(group: group) {
self.networkRequest(sleepTime:2, closure: {
print("3")
semaphore.signal()
})
}
group.notify(queue: queue) {
semaphore.wait()
semaphore.wait()
semaphore.wait()
print("all done")
}
}
多個(gè)相關(guān)請(qǐng)求順序執(zhí)行
有時(shí)候開(kāi)發(fā)中也會(huì)遇到利用網(wǎng)絡(luò)請(qǐng)求A返回的的數(shù)據(jù)來(lái)進(jìn)行網(wǎng)絡(luò)請(qǐng)求B痒留,以前也是十分膚淺地利用嵌套兜看,雖然簡(jiǎn)單可行而且可以減少中間變量,但是出現(xiàn)過(guò)多嵌套代碼不易debug狭瞎。這里也是可以利用 DispatchSemaphore
信號(hào)量。網(wǎng)絡(luò)請(qǐng)求A完成后 signal
搏予,而網(wǎng)絡(luò)請(qǐng)求B發(fā)起前 wait
熊锭。
// 利用 semaphore 來(lái)控制
func gcd_line_request() {
let semaphore = DispatchSemaphore.init(value: 0)
let group = DispatchGroup.init()
let queue = DispatchQueue.global()
queue.async(group: group) {
self.networkRequest(sleepTime:1, closure: {
print("1")
semaphore.signal()
})
}
queue.async(group: group) {
semaphore.wait()
self.networkRequest(sleepTime:2, closure: {
print("2")
})
}
}
當(dāng)然也可以利用RAC,在A請(qǐng)求完成后發(fā)送信號(hào)喚醒B執(zhí)行即可雪侥,還可以傳遞參數(shù)碗殷。
// 利用 rac 來(lái)控制
func rac_request() {
let (requestA, observerA) = Signal<Bool, NoError>.pipe()
self.networkRequest(sleepTime: 1) {
print("網(wǎng)絡(luò)請(qǐng)求A完成")
observerA.send(value: true) //網(wǎng)絡(luò)請(qǐng)求完成并且是成功的
observerA.sendCompleted()
}
requestA.observeValues { (success) in
if success {
self.networkRequest(sleepTime: 1, closure: {
print("網(wǎng)絡(luò)請(qǐng)求B完成")
})
}
}
}
順便說(shuō)說(shuō)柵欄函數(shù) dispatch_barrier_async
這是可以在并行隊(duì)列中插入一個(gè)操作,例如任務(wù)1234都是并行的速缨,如果在代碼中12和34插入dispatch_barrier_async
則可以保證 dispatch_barrier_async
中的內(nèi)容在12后锌妻、34前執(zhí)行。
func test() {
let group = DispatchGroup.init()
let queue = DispatchQueue.init(label: "xQ")
queue.async(group: group) {
print("1 start")
}
queue.async(group: group) {
print("2 start")
}
queue.async(group: group, flags: .barrier) {
print("5 start")
}
queue.async(group: group) {
print("3 start")
}
queue.async(group: group) {
print("4 start")
}
}
這里可以保證輸出為 12/21 5 34/43
這樣的順序旬牲,5肯定夾在中間仿粹。
不過(guò)注意這里有個(gè)十分坑的地方:對(duì)于 DispatchQueue.global()
這個(gè)函數(shù)不起效搁吓!
在 swift 文檔中并沒(méi)有提到,我是在看 OC 文檔中發(fā)現(xiàn)的解釋吭历,對(duì) DispatchQueue.global()
不起效堕仔,僅相當(dāng)于 dispatch_async
,dispatch_barrier_async
只對(duì)自己創(chuàng)建的隊(duì)列才生效晌区。
The queue you specify should be a concurrent queue that you create yourself using the dispatch_queue_create function. If the queue you pass to this function is a serial queue or one of the global concurrent queues, this function behaves like the dispatch_async function.
最后說(shuō)一句摩骨,swift3 中的 GCD
幾乎全部換新,具體使用必須看官方文件朗若。