
等待多個并發(fā)請求同步回調(diào)
例如同時(shí)發(fā)起網(wǎng)絡(luò)請求A,網(wǎng)絡(luò)請求B,網(wǎng)絡(luò)請求C,需等待A,B,C都返回了才進(jìn)行回調(diào)。曾經(jīng)是利用 block 嵌套,就是A回調(diào)嵌B,B回調(diào)嵌C,十分膚淺的做法╮(╯▽╰)╭
這里介紹 GCD 中的兩種方法,第一種是利用 DispatchGroup 的 enter 和 leave,第二種是利用 DispatchSemaphore 信號量。
以下代碼是基于 swift 3.0.2。
首先先創(chuàng)建一個模擬請求。
// 假裝是網(wǎng)絡(luò)請求
func networkRequest(sleepTime: Int, closure: @escaping ()->Void) -> Void {
DispatchQueue.global().async {
Thread.sleep(forTimeInterval: TimeInterval(sleepTime))
// 假裝是成功回調(diào)
closure()
}
}
第一種方法利用 enter&leave。需要注意的是 enter&leave 必須成對出現(xiàn),enter 少了會崩潰,leave 少了則永遠(yuǎn)不會執(zhí)行 notify 函數(shù)。
這種方法的原理就是:
- 請求前
enter。 - 請求完成后
leave。
// 利用 enter/leave 來控制
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")
}
}
第二種方法是利用信號量的 wait&signal,簡單來說就是:signal 就是釋放信號即信號量 +1,wait 就是等待信號即信號量-1。wait&signal也是必須成對出現(xiàn)。
這種方法的原理就是:
- 創(chuàng)建0信號量。
- 請求完成釋放信號,使得信號量+1。
-
notify的回調(diào)操作前加入wait操作(多少請求就加多少wait,這要做為了得到足夠的信號量才能執(zhí)行wait下面的代碼)。
// 利用 semaphore 來控制
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")
}
}
多個相關(guān)請求順序執(zhí)行
有時(shí)候開發(fā)中也會遇到利用網(wǎng)絡(luò)請求A返回的的數(shù)據(jù)來進(jìn)行網(wǎng)絡(luò)請求B,以前也是十分膚淺地利用嵌套,雖然簡單可行而且可以減少中間變量,但是出現(xiàn)過多嵌套代碼不易debug。這里也是可以利用 DispatchSemaphore 信號量。網(wǎng)絡(luò)請求A完成后 signal,而網(wǎng)絡(luò)請求B發(fā)起前 wait。
// 利用 semaphore 來控制
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請求完成后發(fā)送信號喚醒B執(zhí)行即可,還可以傳遞參數(shù)。
// 利用 rac 來控制
func rac_request() {
let (requestA, observerA) = Signal<Bool, NoError>.pipe()
self.networkRequest(sleepTime: 1) {
print("網(wǎng)絡(luò)請求A完成")
observerA.send(value: true) //網(wǎng)絡(luò)請求完成并且是成功的
observerA.sendCompleted()
}
requestA.observeValues { (success) in
if success {
self.networkRequest(sleepTime: 1, closure: {
print("網(wǎng)絡(luò)請求B完成")
})
}
}
}
順便說說柵欄函數(shù) dispatch_barrier_async
這是可以在并行隊(duì)列中插入一個操作,例如任務(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肯定夾在中間。
不過注意這里有個十分坑的地方:對于 DispatchQueue.global() 這個函數(shù)不起效!
在 swift 文檔中并沒有提到,我是在看 OC 文檔中發(fā)現(xiàn)的解釋,對 DispatchQueue.global() 不起效,僅相當(dāng)于 dispatch_async,dispatch_barrier_async 只對自己創(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.
最后說一句,swift3 中的 GCD 幾乎全部換新,具體使用必須看官方文件。