Ver3.0 ๐ Grand Central Dispatch์ OperationQueue๋ก ๋ฉํฐ์ค๋ ๋ฉ ์ ๋ณตํ๊ธฐ

๐ Grand Central Dispatch์ OperationQueue๋ก ๋ฉํฐ์ค๋ ๋ฉ ์ ๋ณตํ๊ธฐ
์๋
! ์ค๋์ iOS์ macOS ๊ฐ๋ฐ์์ ๋นผ๋์ ์ ์๋ ๋ฉํฐ์ค๋ ๋ฉ ์ด์ผ๊ธฐ๋ฅผ ํด๋ณผ๊น ํด. ๐ฏ
ํน์ ์ฑ์ ๋ง๋ค๋ค๊ฐ ํ๋ฉด์ด ๋ฒ๋ฒ
๊ฑฐ๋ฆฌ๊ฑฐ๋, ๋ฐ์ดํฐ๋ฅผ ๋ค์ด๋ก๋ํ๋ ๋์ ์ฑ์ด ๋ฉ์ถฐ๋ฒ๋ฆฐ ๊ฒฝํ ์์ด?
๊ทธ๋ด ๋ ํ์ํ ๊ฒ ๋ฐ๋ก ๋ฉํฐ์ค๋ ๋ฉ์ด์ผ. ์ฌ๋ฌ ์์
์ ๋์์ ์ฒ๋ฆฌํด์ ์ฑ์ ๋ถ๋๋ฝ๊ฒ ๋ง๋๋ ๋ง๋ฒ ๊ฐ์ ๊ธฐ์ ์ด์ง.
Apple์ ๊ฐ๋ฐ์๋ค์๊ฒ ๋ ๊ฐ์ง ๊ฐ๋ ฅํ ๋๊ตฌ๋ฅผ ์ ๊ณตํด. ๋ฐ๋ก Grand Central Dispatch(GCD)์ OperationQueue์ผ.
์ด ๋์ ๋ฉํฐ์ค๋ ๋ฉ์ ์ฝ๊ฒ ๊ตฌํํ ์ ์๊ฒ ๋์์ฃผ๋๋ฐ, ๊ฐ๊ฐ ํน์ง์ด ๋ฌ๋ผ์ ์ํฉ์ ๋ง๊ฒ ์ ํํด์ผ ํด.
์ฌ๋ฅ๋ท์์ ํ๋ก๊ทธ๋๋ฐ ์ง์์ ๊ณต์ ํ๋ ๋ง์ ๊ฐ๋ฐ์๋ค๋ ์ด ์ฃผ์ ์ ๋ํด ์์ฃผ ์ง๋ฌธํ๊ณ ๋ต๋ณ์ ๋๋๊ณค ํ์ง. ๐
๋ฉํฐ์ค๋ ๋ฉ์ด ๋ญ๊ธธ๋? ๐ค
๋จผ์ ๊ธฐ๋ณธ๋ถํฐ ์ง๊ณ ๋์ด๊ฐ์. ๋ฉํฐ์ค๋ ๋ฉ์ ๋ง ๊ทธ๋๋ก ์ฌ๋ฌ ๊ฐ์ ์ค๋ ๋๋ฅผ ์ฌ์ฉํ๋ ๊ฑฐ์ผ.
์ค๋ ๋๋ ํ๋ก๊ทธ๋จ์ด ์์
์ ์คํํ๋ ๊ฐ์ฅ ์์ ๋จ์๋ผ๊ณ ์๊ฐํ๋ฉด ๋ผ.
์ฐ๋ฆฌ๊ฐ ๋ง๋๋ ์ฑ์ ๊ธฐ๋ณธ์ ์ผ๋ก ๋ฉ์ธ ์ค๋ ๋(Main Thread)์์ ์คํ๋๋๋ฐ, ์ด ์น๊ตฌ๊ฐ UI๋ฅผ ๋ด๋นํด.
๋ฉ์ธ ์ค๋ ๋์ ์ญํ ๐จ
๋ฉ์ธ ์ค๋ ๋๋ ํ๋ฉด์ ๋ณด์ด๋ ๋ชจ๋ ๊ฒ์ ๊ด๋ฆฌํด. ๋ฒํผ์ ๋๋ฅด๋ฉด ๋ฐ์ํ๊ณ , ์คํฌ๋กคํ๋ฉด ํ๋ฉด์ด ์์ง์ด๊ณ ,
์ ๋๋ฉ์ด์
์ด ๋ถ๋๋ฝ๊ฒ ์คํ๋๋ ๊ฒ๋ ๋ค ๋ฉ์ธ ์ค๋ ๋ ๋๋ถ์ด์ผ.
๊ทธ๋ฐ๋ฐ ๋ง์ฝ ๋ฉ์ธ ์ค๋ ๋์์ ์๊ฐ์ด ์ค๋ ๊ฑธ๋ฆฌ๋ ์์
(๋คํธ์ํฌ ์์ฒญ, ๋์ฉ๋ ํ์ผ ์ฒ๋ฆฌ ๋ฑ)์ ํ๋ฉด ์ด๋ป๊ฒ ๋ ๊น?
์ฑ์ด ๋ฉ์ถ ๊ฒ์ฒ๋ผ ๋ณด์ด๊ณ , ์ฌ์ฉ์๋ ๋ต๋ตํจ์ ๋๋ผ๊ฒ ๋ผ. ๐ฐ
๊ทธ๋์ ์ฐ๋ฆฌ๋ ๋ฌด๊ฑฐ์ด ์์
์ ๋ฐฑ๊ทธ๋ผ์ด๋ ์ค๋ ๋๋ก ๋ณด๋ด์ผ ํด.
๋ฉ์ธ ์ค๋ ๋๋ UI๋ง ์ ๊ฒฝ ์ฐ๊ฒ ํ๊ณ , ๋ค๋ฅธ ์์
๋ค์ ๋ณ๋์ ์ค๋ ๋์์ ์ฒ๋ฆฌํ๋ ๊ฑฐ์ง.
์ด๋ ๊ฒ ํ๋ฉด ์ฑ์ด ํญ์ ๋ถ๋๋ฝ๊ฒ ๋์ํ ์ ์์ด.
Grand Central Dispatch(GCD) ํํค์น๊ธฐ ๐
GCD๋ Apple์ด 2009๋
์ ๋์
ํ ๋ฉํฐ์ค๋ ๋ฉ ๊ธฐ์ ์ด์ผ.
C ์ธ์ด ๊ธฐ๋ฐ์ผ๋ก ๋ง๋ค์ด์ก์ง๋ง, Swift์์๋ ์์ฃผ ํธํ๊ฒ ์ฌ์ฉํ ์ ์์ด.
GCD์ ํต์ฌ ๊ฐ๋
์ Dispatch Queue์ผ. ์์
์ ํ์ ๋ฃ์ผ๋ฉด ์์คํ
์ด ์์์ ์ ์ ํ ์ค๋ ๋์ ๋ฐฐ์ ํด์ ์คํํด์ค.
Dispatch Queue์ ์ข ๋ฅ ๐ฆ
GCD์๋ ํฌ๊ฒ ๋ ๊ฐ์ง ํ์ ์ ํ๊ฐ ์์ด.
Serial Queue (์ง๋ ฌ ํ)
ํ ๋ฒ์ ํ๋์ ์์
๋ง ์คํํด. ๋ง์น ์ํ ์ฐฝ๊ตฌ์์ ํ ๋ช
์ฉ ์์๋๋ก ์
๋ฌด๋ฅผ ๋ณด๋ ๊ฒ๊ณผ ๊ฐ์.
๋จผ์ ๋ค์ด์จ ์์
์ด ๋๋์ผ ๋ค์ ์์
์ด ์์๋ผ.
ํน์ง: ์์ ๋ณด์ฅ, ๋๊ธฐํ ๋ฌธ์ ์์ โ
Concurrent Queue (๋์ ํ)
์ฌ๋ฌ ์์
์ ๋์์ ์คํํ ์ ์์ด. ์ฌ๋ฌ ์ํ ์ฐฝ๊ตฌ๊ฐ ๋์์ ์ด๋ ค์๋ ๊ฒ๊ณผ ๋น์ทํด.
์์
๋ค์ด ๋์์ ์์๋์ง๋ง, ๋๋๋ ์์๋ ๋ณด์ฅ๋์ง ์์.
ํน์ง: ๋น ๋ฅธ ์ฒ๋ฆฌ, ์์ ๋ณด์ฅ ์ ๋จ โก
GCD ์ฌ์ฉ๋ฒ - ๊ธฐ๋ณธํธ ๐ป
์, ์ด์ ์ค์ ์ฝ๋๋ฅผ ๋ณผ๊น? Swift์์ GCD๋ฅผ ์ฌ์ฉํ๋ ๊ฑด ์ ๋ง ๊ฐ๋จํด.
// ๋ฉ์ธ ํ์์ UI ์
๋ฐ์ดํธ
DispatchQueue.main.async {
self.label.text = "์์
์๋ฃ!"
self.activityIndicator.stopAnimating()
}
// ๊ธ๋ก๋ฒ ํ์์ ๋ฐฑ๊ทธ๋ผ์ด๋ ์์
DispatchQueue.global(qos: .background).async {
// ์๊ฐ์ด ์ค๋ ๊ฑธ๋ฆฌ๋ ์์
let result = self.processHeavyData()
// ๊ฒฐ๊ณผ๋ฅผ ๋ฉ์ธ ์ค๋ ๋์์ ์ฒ๋ฆฌ
DispatchQueue.main.async {
self.displayResult(result)
}
}
์ ์ฝ๋๋ฅผ ๋ณด๋ฉด DispatchQueue.main๊ณผ DispatchQueue.global์ด ๋ณด์ด์ง?
main์ ๋ฉ์ธ ์ค๋ ๋๋ฅผ ์๋ฏธํ๊ณ , global์ ์์คํ
์ด ๊ด๋ฆฌํ๋ ๊ธ๋ก๋ฒ ํ๋ฅผ ์๋ฏธํด.
๐ก QoS(Quality of Service)๋?
์์
์ ์ฐ์ ์์๋ฅผ ์ง์ ํ๋ ๊ฑฐ์ผ. ์ข
๋ฅ๋ ๋ค์๊ณผ ๊ฐ์:
โข userInteractive: ์ฌ์ฉ์์ ์ง์ ์ํธ์์ฉ (์ต๊ณ ์ฐ์ ์์) ๐ด
โข userInitiated: ์ฌ์ฉ์๊ฐ ์์ํ ์์
(๋์ ์ฐ์ ์์) ๐
โข default: ๊ธฐ๋ณธ๊ฐ ๐ก
โข utility: ์๊ฐ์ด ์ข ๊ฑธ๋ ค๋ ๋๋ ์์
๐ข
โข background: ๋ฐฑ๊ทธ๋ผ์ด๋ ์์
(๋ฎ์ ์ฐ์ ์์) ๐ต
์์คํ
์ ์ด ์ฐ์ ์์๋ฅผ ๋ณด๊ณ CPU ์์์ ๋ฐฐ๋ถํด.
์ปค์คํ ํ ๋ง๋ค๊ธฐ ๐ ๏ธ
์์คํ ์ ๊ณต ํ ๋ง๊ณ ์ง์ ํ๋ฅผ ๋ง๋ค ์๋ ์์ด. ํน์ ์์ ์ ์ํ ์ ์ฉ ํ๊ฐ ํ์ํ ๋ ์ ์ฉํ์ง.
// Serial Queue ์์ฑ
let serialQueue = DispatchQueue(label: "com.myapp.serialQueue")
// Concurrent Queue ์์ฑ
let concurrentQueue = DispatchQueue(label: "com.myapp.concurrentQueue",
attributes: .concurrent)
// ์ฌ์ฉ ์์
serialQueue.async {
print("์์
1 ์์")
Thread.sleep(forTimeInterval: 2)
print("์์
1 ์๋ฃ")
}
serialQueue.async {
print("์์
2 ์์")
Thread.sleep(forTimeInterval: 1)
print("์์
2 ์๋ฃ")
}
// ์ถ๋ ฅ ์์: ์์
1 ์์ โ ์์
1 ์๋ฃ โ ์์
2 ์์ โ ์์
2 ์๋ฃ
Serial Queue๋ ์์๊ฐ ๋ณด์ฅ๋๋๊น ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ ๊ทผ์ด๋ ํ์ผ ์ฐ๊ธฐ ๊ฐ์ ์์
์ ์ ํฉํด.
์ฌ๋ฌ ์ค๋ ๋๊ฐ ๋์์ ๊ฐ์ ์์์ ์ ๊ทผํ๋ฉด ๋ฌธ์ ๊ฐ ์๊ธธ ์ ์๊ฑฐ๋ . ๐
๋๊ธฐ vs ๋น๋๊ธฐ ์คํ โฑ๏ธ
GCD์์๋ ์์ ์ ๋๊ธฐ(sync) ๋๋ ๋น๋๊ธฐ(async)๋ก ์คํํ ์ ์์ด.
async (๋น๋๊ธฐ)
์์
์ ํ์ ๋ฃ๊ณ ๋ฐ๋ก ๋ค์ ์ฝ๋๋ก ๋์ด๊ฐ. ์์
์ด ๋๋ ๋๊น์ง ๊ธฐ๋ค๋ฆฌ์ง ์์.
๋๋ถ๋ถ์ ๊ฒฝ์ฐ ์ด๊ฑธ ์ฌ์ฉํด. ์ฑ์ด ๋ฉ์ถ์ง ์์ผ๋๊น! ๐โโ๏ธ
sync (๋๊ธฐ)
์์
์ ํ์ ๋ฃ๊ณ ์๋ฃ๋ ๋๊น์ง ๊ธฐ๋ค๋ ค. ๋ค์ ์ฝ๋๋ ์์
์ด ๋๋ ํ์ ์คํ๋ผ.
์ฃผ์ํ ์ : ๋ฉ์ธ ์ค๋ ๋์์ ๋ฉ์ธ ํ์ sync๋ฅผ ํธ์ถํ๋ฉด ๋ฐ๋๋ฝ์ด ๋ฐ์ํด! โ ๏ธ
// ๋น๋๊ธฐ ์คํ (์ผ๋ฐ์ ์ธ ์ฌ์ฉ)
DispatchQueue.global().async {
print("๋น๋๊ธฐ ์์
์์")
// ๋ฌด๊ฑฐ์ด ์์
print("๋น๋๊ธฐ ์์
์๋ฃ")
}
print("์ด ์ฝ๋๋ ๋ฐ๋ก ์คํ๋จ")
// ๋๊ธฐ ์คํ (์ ์คํ๊ฒ ์ฌ์ฉ)
DispatchQueue.global().sync {
print("๋๊ธฐ ์์
์์")
// ์์
print("๋๊ธฐ ์์
์๋ฃ")
}
print("์ด ์ฝ๋๋ ์ ์์
์ด ๋๋ ํ ์คํ๋จ")
Dispatch Group์ผ๋ก ์ฌ๋ฌ ์์ ๊ด๋ฆฌํ๊ธฐ ๐ฅ
์ฌ๋ฌ ๊ฐ์ ๋น๋๊ธฐ ์์ ์ด ๋ชจ๋ ๋๋ฌ์ ๋ ๋ญ๊ฐ๋ฅผ ํ๊ณ ์ถ๋ค๋ฉด? DispatchGroup์ ์ฌ์ฉํ๋ฉด ๋ผ!
let group = DispatchGroup()
// ์ฒซ ๋ฒ์งธ ์์
group.enter()
DispatchQueue.global().async {
print("์ด๋ฏธ์ง 1 ๋ค์ด๋ก๋ ์ค...")
Thread.sleep(forTimeInterval: 2)
print("์ด๋ฏธ์ง 1 ์๋ฃ")
group.leave()
}
// ๋ ๋ฒ์งธ ์์
group.enter()
DispatchQueue.global().async {
print("์ด๋ฏธ์ง 2 ๋ค์ด๋ก๋ ์ค...")
Thread.sleep(forTimeInterval: 3)
print("์ด๋ฏธ์ง 2 ์๋ฃ")
group.leave()
}
// ์ธ ๋ฒ์งธ ์์
group.enter()
DispatchQueue.global().async {
print("์ด๋ฏธ์ง 3 ๋ค์ด๋ก๋ ์ค...")
Thread.sleep(forTimeInterval: 1)
print("์ด๋ฏธ์ง 3 ์๋ฃ")
group.leave()
}
// ๋ชจ๋ ์์
์ด ์๋ฃ๋๋ฉด ์คํ
group.notify(queue: .main) {
print("๋ชจ๋ ์ด๋ฏธ์ง ๋ค์ด๋ก๋ ์๋ฃ! ๐")
// UI ์
๋ฐ์ดํธ
}
enter()๋ก ์์
์ ์์ํ๊ณ , leave()๋ก ์๋ฃ๋ฅผ ์๋ ค์ค.
๋ชจ๋ ์์
์ด ๋๋๋ฉด notify์ ๋ฑ๋กํ ํด๋ก์ ๊ฐ ์คํ๋ผ. ์ ๋ง ํธ๋ฆฌํ์ง? ๐
โ ๏ธ ์ฃผ์์ฌํญ
enter()์ leave()์ ํธ์ถ ํ์๋ ๋ฐ๋์ ๊ฐ์์ผ ํด!
enter()๋ฅผ 3๋ฒ ํ์ผ๋ฉด leave()๋ 3๋ฒ ํด์ผ notify๊ฐ ์คํ๋ผ.
๋ง์ฝ ๊ท ํ์ด ์ ๋ง์ผ๋ฉด notify๊ฐ ์์ํ ์คํ๋์ง ์๊ฑฐ๋, ์ฑ์ด ํฌ๋์๋ ์ ์์ด.
Dispatch Barrier๋ก ๋๊ธฐํํ๊ธฐ ๐ง
Concurrent Queue์์ ํน์ ์์
๋ง ๋
์ ์ ์ผ๋ก ์คํํ๊ณ ์ถ์ ๋๊ฐ ์์ด.
์๋ฅผ ๋ค์ด, ์ฌ๋ฌ ์ค๋ ๋๊ฐ ๋ฐ์ดํฐ๋ฅผ ์ฝ๋ ๊ฑด ๊ด์ฐฎ์ง๋ง, ์ฐ๊ธฐ ์์
์ ํผ์ ํด์ผ ํ ๋ ๋ง์ด์ผ.
let concurrentQueue = DispatchQueue(label: "com.myapp.dataQueue",
attributes: .concurrent)
var data: [String] = []
// ์ฝ๊ธฐ ์์
(๋์ ์คํ ๊ฐ๋ฅ)
func readData() {
concurrentQueue.async {
print("๋ฐ์ดํฐ ์ฝ๊ธฐ: \(data)")
}
}
// ์ฐ๊ธฐ ์์
(๋
์ ์คํ)
func writeData(_ value: String) {
concurrentQueue.async(flags: .barrier) {
print("๋ฐ์ดํฐ ์ฐ๊ธฐ: \(value)")
data.append(value)
}
}
// ์ฌ์ฉ
readData() // ๋์ ์คํ ๊ฐ๋ฅ
readData() // ๋์ ์คํ ๊ฐ๋ฅ
writeData("์ ๋ฐ์ดํฐ") // ๋ค๋ฅธ ์์
์ด ๋๋ ๋๊น์ง ๋๊ธฐ
readData() // writeData๊ฐ ๋๋ ํ ์คํ
barrier ํ๋๊ทธ๋ฅผ ์ฌ์ฉํ๋ฉด ํด๋น ์์
์ด ์คํ๋ ๋ ๋ค๋ฅธ ์์
๋ค์ด ๋ชจ๋ ๋๋ ๋๊น์ง ๊ธฐ๋ค๋ ค.
๊ทธ๋ฆฌ๊ณ barrier ์์
์ด ๋๋๋ฉด ๋ค์ ๋์ ์คํ์ด ๊ฐ๋ฅํด์ ธ. ๋ฐ์ดํฐ ๋ฌด๊ฒฐ์ฑ์ ์งํค๋ ์ข์ ๋ฐฉ๋ฒ์ด์ผ! ๐ก๏ธ
OperationQueue ๊น์ด ์์๋ณด๊ธฐ ๐ฏ
์ด์ OperationQueue๋ก ๋์ด๊ฐ๋ณผ๊น? GCD๊ฐ ๊ฐ๋จํ๊ณ ๋น ๋ฅด๋ค๋ฉด, OperationQueue๋ ์ข ๋ ๊ณ ๊ธ ๊ธฐ๋ฅ์ ์ ๊ณตํด.
๊ฐ์ฒด ์งํฅ์ ์ธ ์ ๊ทผ ๋ฐฉ์์ด๋ผ ๋ณต์กํ ์์
ํ๋ฆ์ ๊ด๋ฆฌํ๊ธฐ์ ์ข์.
Operation์ ๊ธฐ๋ณธ ๊ฐ๋ ๐
OperationQueue๋ Operation ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํด. Operation์ ์คํํ ์์
์ ์บก์ํํ ๊ฐ์ฒด์ผ.
๋ ๊ฐ์ง ๋ฐฉ๋ฒ์ผ๋ก ๋ง๋ค ์ ์์ด:
BlockOperation
ํด๋ก์ ๋ฅผ ์ฌ์ฉํด์ ๊ฐ๋จํ๊ฒ ๋ง๋๋ ๋ฐฉ๋ฒ์ด์ผ.
GCD์ ํด๋ก์ ๋ฐฉ์๊ณผ ๋น์ทํ์ง๋ง, Operation์ ๊ธฐ๋ฅ์ ์ฌ์ฉํ ์ ์์ด.
let operation = BlockOperation {
print("์์
์คํ")
}
Custom Operation
Operation ํด๋์ค๋ฅผ ์์๋ฐ์์ ๋ง๋๋ ๋ฐฉ๋ฒ์ด์ผ.
๋ณต์กํ ๋ก์ง์ด๋ ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ์์
์ ์ ํฉํด.
class MyOperation: Operation {
override func main() {
print("์ปค์คํ
์์
")
}
}
OperationQueue ์ฌ์ฉํ๊ธฐ ๐
// OperationQueue ์์ฑ
let operationQueue = OperationQueue()
// ๋์ ์คํ ๊ฐ์ ์ค์ (๊ธฐ๋ณธ๊ฐ์ ์์คํ
์ด ๊ฒฐ์ )
operationQueue.maxConcurrentOperationCount = 3
// BlockOperation ์ถ๊ฐ
let operation1 = BlockOperation {
print("Operation 1 ์์")
Thread.sleep(forTimeInterval: 2)
print("Operation 1 ์๋ฃ")
}
let operation2 = BlockOperation {
print("Operation 2 ์์")
Thread.sleep(forTimeInterval: 1)
print("Operation 2 ์๋ฃ")
}
// ํ์ ์ถ๊ฐ
operationQueue.addOperation(operation1)
operationQueue.addOperation(operation2)
// ๋๋ ํด๋ก์ ๋ก ๋ฐ๋ก ์ถ๊ฐ
operationQueue.addOperation {
print("๊ฐ๋จํ ์์
")
}
maxConcurrentOperationCount๋ฅผ ์ค์ ํ๋ฉด ๋์์ ์คํ๋๋ ์์
์๋ฅผ ์ ํํ ์ ์์ด.
1๋ก ์ค์ ํ๋ฉด Serial Queue์ฒ๋ผ ๋์ํ๊ณ , ๊ธฐ๋ณธ๊ฐ(-1)์ ์์คํ
์ด ์์์ ๊ฒฐ์ ํด.
์์กด์ฑ ๊ด๋ฆฌ - OperationQueue์ ๊ฐ๋ ฅํ ๋ฌด๊ธฐ ๐
OperationQueue์ ๊ฐ์ฅ ํฐ ์ฅ์ ์ค ํ๋๊ฐ ๋ฐ๋ก ์์กด์ฑ ๊ด๋ฆฌ์ผ.
"A ์์
์ด ๋๋์ผ B ์์
์ ์์ํ๋ค"๋ ์์ ์์๋ฅผ ์ฝ๊ฒ ์ง์ ํ ์ ์์ด.
let downloadOperation = BlockOperation {
print("์ด๋ฏธ์ง ๋ค์ด๋ก๋ ์ค...")
Thread.sleep(forTimeInterval: 2)
print("๋ค์ด๋ก๋ ์๋ฃ")
}
let filterOperation = BlockOperation {
print("ํํฐ ์ ์ฉ ์ค...")
Thread.sleep(forTimeInterval: 1)
print("ํํฐ ์ ์ฉ ์๋ฃ")
}
let uploadOperation = BlockOperation {
print("์
๋ก๋ ์ค...")
Thread.sleep(forTimeInterval: 1)
print("์
๋ก๋ ์๋ฃ")
}
// ์์กด์ฑ ์ค์
filterOperation.addDependency(downloadOperation) // ๋ค์ด๋ก๋ ํ ํํฐ
uploadOperation.addDependency(filterOperation) // ํํฐ ํ ์
๋ก๋
let queue = OperationQueue()
queue.addOperations([uploadOperation, filterOperation, downloadOperation],
waitUntilFinished: false)
// ์คํ ์์: ๋ค์ด๋ก๋ โ ํํฐ โ ์
๋ก๋ (์์กด์ฑ์ ๋ฐ๋ผ ์๋ ์ ๋ ฌ!)
ํ์ ์ถ๊ฐํ๋ ์์๋ ์๊ด์์ด. ์์กด์ฑ๋ง ์ ๋๋ก ์ค์ ํ๋ฉด ์์คํ
์ด ์์์ ์ฌ๋ฐ๋ฅธ ์์๋ก ์คํํด์ค! ๐ฏ
์ด๊ฑด GCD๋ก๋ ๊ตฌํํ๊ธฐ ๊น๋ค๋ก์ด ๊ธฐ๋ฅ์ด์ผ. ์ฌ๋ฅ๋ท์ ๊ฐ๋ฐ์ ์ปค๋ฎค๋ํฐ์์๋ ์ด ๊ธฐ๋ฅ ๋๋ฌธ์ OperationQueue๋ฅผ ์ ํธํ๋ ์ฌ๋๋ค์ด ๋ง์.
์์ ์ทจ์ํ๊ธฐ โ
OperationQueue์ ๋ ๋ค๋ฅธ ์ฅ์ ์ ์์
์ ์ทจ์ํ ์ ์๋ค๋ ๊ฑฐ์ผ.
์ฌ์ฉ์๊ฐ ๋ค์ด๋ก๋๋ฅผ ์ค๋จํ๊ฑฐ๋, ํ๋ฉด์ ๋ฒ์ด๋ฌ์ ๋ ๋ถํ์ํ ์์
์ ๋ฉ์ถ ์ ์์ด.
class DownloadOperation: Operation {
override func main() {
// ์ทจ์ ํ์ธ
guard !isCancelled else {
print("์์
์ด ์ทจ์๋์์ต๋๋ค")
return
}
print("๋ค์ด๋ก๋ ์์")
for i in 1...10 {
// ์ฃผ๊ธฐ์ ์ผ๋ก ์ทจ์ ํ์ธ
if isCancelled {
print("๋ค์ด๋ก๋ ์ค๋จ๋จ (์งํ๋ฅ : \(i * 10)%)")
return
}
Thread.sleep(forTimeInterval: 0.5)
print("์งํ๋ฅ : \(i * 10)%")
}
print("๋ค์ด๋ก๋ ์๋ฃ")
}
}
let operation = DownloadOperation()
let queue = OperationQueue()
queue.addOperation(operation)
// 2์ด ํ ์ทจ์
DispatchQueue.global().asyncAfter(deadline: .now() + 2) {
operation.cancel()
print("์ทจ์ ์์ฒญ ์ ์ก")
}
isCancelled ํ๋กํผํฐ๋ฅผ ์ฃผ๊ธฐ์ ์ผ๋ก ํ์ธํด์ ์ทจ์ ์์ฒญ์ด ์์ผ๋ฉด ์์
์ ์ค๋จํด.
GCD์์๋ ์ด๋ฐ ์ทจ์ ๊ธฐ๋ฅ์ ์ง์ ๊ตฌํํด์ผ ํ๋๋ฐ, Operation์ ๊ธฐ๋ณธ์ผ๋ก ์ ๊ณตํด์ค์ ํธ๋ฆฌํด! ๐
๐ก ์ทจ์ ์ ์ฃผ์์ฌํญ
cancel()์ ํธ์ถํ๋ค๊ณ ์์
์ด ์ฆ์ ๋ฉ์ถ๋ ๊ฑด ์๋์ผ.
Operation ๋ด๋ถ์์ isCancelled๋ฅผ ํ์ธํ๊ณ ์ง์ ์ข
๋ฃ ์ฒ๋ฆฌ๋ฅผ ํด์ค์ผ ํด.
ํนํ ๊ธด ์์
์ ๊ฒฝ์ฐ, ์ค๊ฐ์ค๊ฐ ์ทจ์ ์ฌ๋ถ๋ฅผ ํ์ธํ๋ ๊ฒ ์ข์.
์ฐ์ ์์ ์ค์ ํ๊ธฐ โญ
Operation์๋ ์ฐ์ ์์๋ฅผ ์ค์ ํ ์ ์์ด. ์ค์ํ ์์ ์ ๋จผ์ ์ฒ๋ฆฌํ๊ณ ์ถ์ ๋ ์ ์ฉํ์ง.
let highPriorityOp = BlockOperation {
print("์ค์ํ ์์
")
}
highPriorityOp.queuePriority = .veryHigh
let normalOp = BlockOperation {
print("์ผ๋ฐ ์์
")
}
normalOp.queuePriority = .normal
let lowPriorityOp = BlockOperation {
print("๋ฎ์ ์ฐ์ ์์ ์์
")
}
lowPriorityOp.queuePriority = .veryLow
let queue = OperationQueue()
queue.addOperations([lowPriorityOp, normalOp, highPriorityOp],
waitUntilFinished: false)
// ์ฐ์ ์์์ ๋ฐ๋ผ ์คํ ์์๊ฐ ์กฐ์ ๋จ
์ฐ์ ์์๋ ๋ค์๊ณผ ๊ฐ์:
โข veryHigh: ๊ฐ์ฅ ๋์
โข high: ๋์
โข normal: ๋ณดํต (๊ธฐ๋ณธ๊ฐ)
โข low: ๋ฎ์
โข veryLow: ๊ฐ์ฅ ๋ฎ์
KVO๋ก ์์ ์ํ ๊ด์ฐฐํ๊ธฐ ๐
Operation์ KVO(Key-Value Observing)๋ฅผ ์ง์ํด์ ์์ ์ ์ํ ๋ณํ๋ฅผ ๊ด์ฐฐํ ์ ์์ด.
let operation = BlockOperation {
print("์์
์คํ ์ค...")
Thread.sleep(forTimeInterval: 2)
print("์์
์๋ฃ")
}
// ์๋ฃ ์ํ ๊ด์ฐฐ
operation.completionBlock = {
print("โ
Operation ์๋ฃ๋จ!")
}
// isFinished ํ๋กํผํฐ ๊ด์ฐฐ
var observer: NSKeyValueObservation?
observer = operation.observe(\.isFinished, options: [.new]) { op, change in
if change.newValue == true {
print("์์
์ด ์ข
๋ฃ๋์์ต๋๋ค")
}
}
let queue = OperationQueue()
queue.addOperation(operation)
completionBlock์ ์์
์ด ๋๋๋ฉด ์๋์ผ๋ก ์คํ๋ผ.
UI ์
๋ฐ์ดํธ๋ ํ์ ์์
์ ์ฒ๋ฆฌํ๊ธฐ ์ข์. ๋จ, ์ด ๋ธ๋ก์ ์์
์ด ์คํ๋ ์ค๋ ๋์์ ์คํ๋๋๊น
UI ์
๋ฐ์ดํธ๊ฐ ํ์ํ๋ฉด ๋ฉ์ธ ์ค๋ ๋๋ก ์ ํํด์ผ ํด!
GCD vs OperationQueue: ์ธ์ ๋ญ ์จ์ผ ํ ๊น? ๐คทโโ๏ธ
์, ์ด์ ๋ ๊ฐ์ง ๋ฐฉ๋ฒ์ ๋ค ๋ฐฐ์ ์ผ๋ ์ ํ์ ๊ธฐ๋ก์ ์ฐ์ด.
"๋๋์ฒด ์ธ์ GCD๋ฅผ ์ฐ๊ณ , ์ธ์ OperationQueue๋ฅผ ์จ์ผ ํ์ง?" ๐ค
๊ฐ๊ฐ์ ์ฅ๋จ์ ์ ์ ๋ฆฌํด๋ณผ๊ฒ.
GCD๋ฅผ ์ ํํด์ผ ํ ๋ โก
โ
๊ฐ๋จํ๊ณ ๋น ๋ฅธ ๋น๋๊ธฐ ์์
โ
์ฝ๋๊ฐ ์งง๊ณ ๋ช
ํํ ๋
โ
์ฑ๋ฅ์ด ์ค์ํ ๊ฒฝ์ฐ
โ
ํด๋ก์ ๊ธฐ๋ฐ์ผ๋ก ์ถฉ๋ถํ ๋
โ
์์คํ
๋ ๋ฒจ์ ์ต์ ํ๊ฐ ํ์ํ ๋
์์:
โข API ํธ์ถ ํ UI ์
๋ฐ์ดํธ
โข ์ด๋ฏธ์ง ๋ค์ด๋ก๋
โข ๊ฐ๋จํ ๋ฐฑ๊ทธ๋ผ์ด๋ ๊ณ์ฐ
โข ํ์ด๋จธ ๊ธฐ๋ฐ ์์
OperationQueue๋ฅผ ์ ํํด์ผ ํ ๋ ๐ฏ
โ
๋ณต์กํ ์์
ํ๋ฆ ๊ด๋ฆฌ
โ
์์
๊ฐ ์์กด์ฑ์ด ์์ ๋
โ
์์
์ทจ์๊ฐ ํ์ํ ๋
โ
์ฐ์ ์์ ๊ด๋ฆฌ๊ฐ ํ์ํ ๋
โ
์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ์์
๋จ์
์์:
โข ์ฌ๋ฌ ๋จ๊ณ์ ๋ฐ์ดํฐ ์ฒ๋ฆฌ
โข ๋์ฉ๋ ํ์ผ ๋ค์ด๋ก๋ (์ทจ์ ๊ฐ๋ฅ)
โข ๋ณต์กํ ์ด๋ฏธ์ง ์ฒ๋ฆฌ ํ์ดํ๋ผ์ธ
โข ๋ฐฐ์น ์์
๊ด๋ฆฌ
๐ ์ค๋ฌด ํ
๋๋ถ๋ถ์ ๊ฒฝ์ฐ GCD๋ก ์์ํ๋ ๊ฒ ์ข์. ๊ฐ๋จํ๊ณ ๋น ๋ฅด๊ฑฐ๋ .
๊ทธ๋ฌ๋ค๊ฐ ์์
์ด ๋ณต์กํด์ง๊ฑฐ๋, ์์กด์ฑ ๊ด๋ฆฌ๊ฐ ํ์ํ๊ฑฐ๋, ์ทจ์ ๊ธฐ๋ฅ์ด ํ์ํ๋ฉด OperationQueue๋ก ์ ํํด.
๋ ๊ฐ์ง๋ฅผ ์์ด ์ฐ๋ ๊ฒ๋ ์ ํ ๋ฌธ์ ์์ด! ์ํฉ์ ๋ง๊ฒ ์ ํํ๋ฉด ๋ผ. ๐
์ค์ ์์ : ์ด๋ฏธ์ง ๋ค์ด๋ก๋ ์ฑ ๋ง๋ค๊ธฐ ๐ฑ
์ด๋ก ๋ง ๋ฐฐ์ฐ๋ฉด ์ฌ๋ฏธ์์ผ๋๊น, ์ค์ ๋ก ๋์ํ๋ ์์ ๋ฅผ ๋ง๋ค์ด๋ณผ๊น?
์ฌ๋ฌ ์ด๋ฏธ์ง๋ฅผ ๋์์ ๋ค์ด๋ก๋ํ๊ณ , ์งํ ์ํฉ์ ๋ณด์ฌ์ฃผ๊ณ , ์ทจ์๋ ๊ฐ๋ฅํ ์ฑ์ด์ผ.
GCD ๋ฒ์ ๐
class ImageDownloaderGCD {
func downloadImages(urls: [URL], completion: @escaping ([UIImage]) -> Void) {
var images: [UIImage] = []
let group = DispatchGroup()
let queue = DispatchQueue(label: "com.myapp.imageDownload",
attributes: .concurrent)
for url in urls {
group.enter()
queue.async {
if let data = try? Data(contentsOf: url),
let image = UIImage(data: data) {
// ์ค๋ ๋ ์์ ํ๊ฒ ๋ฐฐ์ด์ ์ถ๊ฐ
DispatchQueue.main.async {
images.append(image)
group.leave()
}
} else {
group.leave()
}
}
}
group.notify(queue: .main) {
completion(images)
}
}
}
// ์ฌ์ฉ ์์
let downloader = ImageDownloaderGCD()
let urls = [
URL(string: "https://example.com/image1.jpg")!,
URL(string: "https://example.com/image2.jpg")!,
URL(string: "https://example.com/image3.jpg")!
]
downloader.downloadImages(urls: urls) { images in
print("๋ค์ด๋ก๋ ์๋ฃ: \(images.count)๊ฐ์ ์ด๋ฏธ์ง")
// UI ์
๋ฐ์ดํธ
}
OperationQueue ๋ฒ์ ๐ฏ
class ImageDownloadOperation: Operation {
let url: URL
var downloadedImage: UIImage?
init(url: URL) {
self.url = url
super.init()
}
override func main() {
guard !isCancelled else { return }
if let data = try? Data(contentsOf: url),
let image = UIImage(data: data) {
guard !isCancelled else { return }
downloadedImage = image
}
}
}
class ImageDownloaderOperation {
private let queue = OperationQueue()
init() {
queue.maxConcurrentOperationCount = 3 // ๋์์ 3๊ฐ๊น์ง
}
func downloadImages(urls: [URL], completion: @escaping ([UIImage]) -> Void) {
var operations: [ImageDownloadOperation] = []
for url in urls {
let operation = ImageDownloadOperation(url: url)
operations.append(operation)
}
// ์๋ฃ ๋ธ๋ก ์ค์
let completionOperation = BlockOperation {
let images = operations.compactMap { $0.downloadedImage }
DispatchQueue.main.async {
completion(images)
}
}
// ๋ชจ๋ ๋ค์ด๋ก๋ ์์
์ ์์กด์ฑ ์ถ๊ฐ
for operation in operations {
completionOperation.addDependency(operation)
}
queue.addOperations(operations, waitUntilFinished: false)
queue.addOperation(completionOperation)
}
func cancelAll() {
queue.cancelAllOperations()
}
}
// ์ฌ์ฉ ์์
let downloader = ImageDownloaderOperation()
let urls = [
URL(string: "https://example.com/image1.jpg")!,
URL(string: "https://example.com/image2.jpg")!,
URL(string: "https://example.com/image3.jpg")!
]
downloader.downloadImages(urls: urls) { images in
print("๋ค์ด๋ก๋ ์๋ฃ: \(images.count)๊ฐ์ ์ด๋ฏธ์ง")
}
// ํ์ํ๋ฉด ์ทจ์
// downloader.cancelAll()
๋ ๋ฒ์ ๋ชจ๋ ๊ฐ์ ๊ธฐ๋ฅ์ ํ์ง๋ง, OperationQueue ๋ฒ์ ์ ์ทจ์ ๊ธฐ๋ฅ์ด ์๊ณ ๋์ ์คํ ๊ฐ์๋ฅผ ์ ํํ ์ ์์ด.
์ํฉ์ ๋ฐ๋ผ ์ ํํ๋ฉด ๋ผ! ๐จ
์ฑ๋ฅ ์ต์ ํ ํ โก
๋ฉํฐ์ค๋ ๋ฉ์ ์ฌ์ฉํ ๋ ์ฑ๋ฅ์ ์ต๋๋ก ๋์ด์ฌ๋ฆฌ๋ ๋ช ๊ฐ์ง ํ์ ์๋ ค์ค๊ฒ.
1. ์ค๋ ๋๋ฅผ ๋๋ฌด ๋ง์ด ๋ง๋ค์ง ๋ง ๐ซ
์ค๋ ๋๋ฅผ ๋ฌดํ์ ๋ง๋ค๋ฉด ์คํ๋ ค ์ฑ๋ฅ์ด ๋จ์ด์ ธ. ์ค๋ ๋ ๊ฐ ์ ํ(Context Switching)์๋ ๋น์ฉ์ด ๋ค๊ฑฐ๋ .
GCD๋ ์์คํ
์ด ์์์ ๊ด๋ฆฌํ์ง๋ง, OperationQueue๋ maxConcurrentOperationCount๋ฅผ ์ ์ ํ ์ค์ ํด์ค.
2. ๋ฉ์ธ ์ค๋ ๋๋ UI๋ง ๐จ
๋ฉ์ธ ์ค๋ ๋์์๋ ์ ๋ ๋ฌด๊ฑฐ์ด ์์
์ ํ์ง ๋ง. 0.1์ด๋ง ๋ธ๋กํน๋์ด๋ ์ฌ์ฉ์๋ ๋ฒ๋ฒ
์์ ๋๊ปด.
๋คํธ์ํฌ ์์ฒญ, ํ์ผ I/O, ๋ณต์กํ ๊ณ์ฐ์ ๋ชจ๋ ๋ฐฑ๊ทธ๋ผ์ด๋๋ก!
3. ์ ์ ํ QoS ์ ํ ๐ฏ
์์
์ ์ค์๋์ ๋ง๋ QoS๋ฅผ ์ ํํด. ๋ชจ๋ ๊ฑธ userInteractive๋ก ํ๋ฉด ์์คํ
์ด ํผ๋์ค๋ฌ์ํด.
โข ์ฌ์ฉ์๊ฐ ๊ธฐ๋ค๋ฆฌ๋ ์์
: userInitiated
โข ๋ฐฑ๊ทธ๋ผ์ด๋ ๋๊ธฐํ: utility ๋๋ background
4. Race Condition ์กฐ์ฌ โ ๏ธ
์ฌ๋ฌ ์ค๋ ๋๊ฐ ๊ฐ์ ๋ฐ์ดํฐ์ ์ ๊ทผํ๋ฉด ๋ฌธ์ ๊ฐ ์๊ธธ ์ ์์ด.
Serial Queue, Dispatch Barrier, ๋๋ Lock์ ์ฌ์ฉํด์ ๋๊ธฐํํด์ค.
Race Condition ์๋ฐฉํ๊ธฐ ๐
// ๋ฌธ์ ๊ฐ ์๋ ์ฝ๋ โ
class Counter {
var count = 0
func increment() {
DispatchQueue.global().async {
self.count += 1 // ์ฌ๋ฌ ์ค๋ ๋์์ ๋์ ์ ๊ทผ!
}
}
}
// ํด๊ฒฐ ๋ฐฉ๋ฒ 1: Serial Queue โ
class SafeCounter1 {
private var count = 0
private let queue = DispatchQueue(label: "com.myapp.counter")
func increment() {
queue.async {
self.count += 1
}
}
func getCount() -> Int {
return queue.sync {
return count
}
}
}
// ํด๊ฒฐ ๋ฐฉ๋ฒ 2: Dispatch Barrier โ
class SafeCounter2 {
private var count = 0
private let queue = DispatchQueue(label: "com.myapp.counter",
attributes: .concurrent)
func increment() {
queue.async(flags: .barrier) {
self.count += 1
}
}
func getCount() -> Int {
return queue.sync {
return count
}
}
}
๋ฐ์ดํฐ ๋ฌด๊ฒฐ์ฑ์ ์ ๋ง ์ค์ํด. ๋ฒ๊ทธ๋ฅผ ์ฐพ๊ธฐ๋ ์ด๋ ต๊ณ , ์ฌํํ๊ธฐ๋ ํ๋ค๊ฑฐ๋ .
์ฒ์๋ถํฐ ์ค๋ ๋ ์์ ํ๊ฒ ์ฝ๋๋ฅผ ์์ฑํ๋ ์ต๊ด์ ๋ค์ด์! ๐ก๏ธ
๋๋ฒ๊น ๊ณผ ํ ์คํธ ๐
๋ฉํฐ์ค๋ ๋ฉ ์ฝ๋๋ ๋๋ฒ๊น
์ด ๊น๋ค๋ก์. ํ์ด๋ฐ์ ๋ฐ๋ผ ๊ฒฐ๊ณผ๊ฐ ๋ฌ๋ผ์ง ์ ์๊ฑฐ๋ .
๋ช ๊ฐ์ง ์ ์ฉํ ๋๋ฒ๊น
ํ์ ์๋ ค์ค๊ฒ.
๐ก Thread Sanitizer ์ฌ์ฉํ๊ธฐ
Xcode์ Thread Sanitizer๋ Race Condition์ ์๋์ผ๋ก ์ฐพ์์ค.
Scheme ์ค์ ์์ "Thread Sanitizer"๋ฅผ ํ์ฑํํ๊ณ ์ฑ์ ์คํํ๋ฉด ๋ผ.
๋ฌธ์ ๊ฐ ๋ฐ๊ฒฌ๋๋ฉด ์ ํํ ์์น๋ฅผ ์๋ ค์ฃผ๋๊น ์ ๋ง ์ ์ฉํด!
// ํ์ฌ ์ค๋ ๋ ํ์ธํ๊ธฐ
func printCurrentThread() {
if Thread.isMainThread {
print("๐จ ๋ฉ์ธ ์ค๋ ๋์์ ์คํ ์ค")
} else {
print("โ๏ธ ๋ฐฑ๊ทธ๋ผ์ด๋ ์ค๋ ๋์์ ์คํ ์ค")
print("์ค๋ ๋ ์ด๋ฆ: \(Thread.current.name ?? "์ด๋ฆ ์์")")
}
}
// ์ฌ์ฉ ์์
DispatchQueue.main.async {
printCurrentThread() // ๋ฉ์ธ ์ค๋ ๋
}
DispatchQueue.global().async {
printCurrentThread() // ๋ฐฑ๊ทธ๋ผ์ด๋ ์ค๋ ๋
}
์ฑ๋ฅ ์ธก์ ํ๊ธฐ โฑ๏ธ
// ์คํ ์๊ฐ ์ธก์
func measureTime(label: String, block: () -> Void) {
let start = CFAbsoluteTimeGetCurrent()
block()
let end = CFAbsoluteTimeGetCurrent()
print("\(label) ์คํ ์๊ฐ: \(String(format: "%.4f", end - start))์ด")
}
// ์ฌ์ฉ ์์
measureTime(label: "์ด๋ฏธ์ง ์ฒ๋ฆฌ") {
// ๋ฌด๊ฑฐ์ด ์์
processImages()
}
// ๋น๋๊ธฐ ์์
์ธก์
let start = CFAbsoluteTimeGetCurrent()
DispatchQueue.global().async {
// ์์
let end = CFAbsoluteTimeGetCurrent()
print("์คํ ์๊ฐ: \(end - start)์ด")
}
์ค๋ฌด์์ ์์ฃผ ํ๋ ์ค์๋ค โ ๏ธ
๊ฒฝํ ๋ง์ ๊ฐ๋ฐ์๋ค๋ ๊ฐ๋ ์ค์ํ๋ ๊ฒ๋ค์ ์ ๋ฆฌํด๋ดค์ด. ๋ฏธ๋ฆฌ ์๊ณ ์์ผ๋ฉด ์๊ฐ์ ๋ง์ด ์ ์ฝํ ์ ์์ด!
์ค์ 1: ๋ฉ์ธ ์ค๋ ๋์์ sync ํธ์ถ ๐ฅ
// ๋ฐ๋๋ฝ ๋ฐ์! โ
DispatchQueue.main.sync {
print("์ด ์ฝ๋๋ ์คํ๋์ง ์์")
}
๋ฉ์ธ ์ค๋ ๋์์ ๋ฉ์ธ ํ์ sync๋ฅผ ํธ์ถํ๋ฉด ๋ฐ๋๋ฝ์ด ๋ฐ์ํด.
๋ฉ์ธ ์ค๋ ๋๋ ์๊ธฐ ์์ ์ด ๋๋๊ธฐ๋ฅผ ๊ธฐ๋ค๋ฆฌ๊ฒ ๋๊ฑฐ๋ . ์ ๋ ํ์ง ๋ง!
์ค์ 2: ๋ฐฑ๊ทธ๋ผ์ด๋์์ UI ์
๋ฐ์ดํธ ๐จ
// ํฌ๋์ ์ํ! โ
DispatchQueue.global().async {
self.label.text = "์๋ฃ" // UI๋ ๋ฉ์ธ ์ค๋ ๋์์๋ง!
}
UIKit์ ์ค๋ ๋ ์์ ํ์ง ์์. ๋ชจ๋ UI ์ ๋ฐ์ดํธ๋ ๋ฉ์ธ ์ค๋ ๋์์ ํด์ผ ํด.
// ์ฌ๋ฐ๋ฅธ ๋ฐฉ๋ฒ โ
DispatchQueue.global().async {
let result = self.heavyCalculation()
DispatchQueue.main.async {
self.label.text = result
}
}
์ค์ 3: Strong Reference Cycle ๐
// ๋ฉ๋ชจ๋ฆฌ ๋์! โ
DispatchQueue.global().async {
self.processData() // self๋ฅผ ๊ฐํ๊ฒ ์ฐธ์กฐ
}
ํด๋ก์ ์์ self๋ฅผ ์บก์ฒํ๋ฉด ์ํ ์ฐธ์กฐ๊ฐ ๋ฐ์ํ ์ ์์ด.
// ์ฌ๋ฐ๋ฅธ ๋ฐฉ๋ฒ โ
DispatchQueue.global().async { [weak self] in
guard let self = self else { return }
self.processData()
}
์ค์ 4: DispatchGroup์ enter/leave ๋ถ๊ท ํ โ๏ธ
// notify๊ฐ ์คํ ์ ๋จ! โ
let group = DispatchGroup()
group.enter()
DispatchQueue.global().async {
// ์์
// leave()๋ฅผ ๊น๋นกํจ!
}
group.notify(queue: .main) {
print("์์ํ ์คํ ์ ๋จ")
}
enter()์ leave()๋ ๋ฐ๋์ ์์ ์ด๋ค์ผ ํด. ํ๋๋ผ๋ ๋น ์ง๋ฉด notify๊ฐ ์คํ ์ ๋ผ!
๊ณ ๊ธ ํจํด: async/await์์ ์กฐํฉ ๐
iOS 13๋ถํฐ Swift์ async/await๊ฐ ๋์
๋๋ฉด์ ๋น๋๊ธฐ ํ๋ก๊ทธ๋๋ฐ์ด ํจ์ฌ ์ฌ์์ก์ด.
GCD, OperationQueue์ ํจ๊ป ์ฌ์ฉํ๋ฉด ๋์ฑ ๊ฐ๋ ฅํด์ ธ!
// GCD๋ฅผ async/await๋ก ๊ฐ์ธ๊ธฐ
func downloadImage(from url: URL) async throws -> UIImage {
return try await withCheckedThrowingContinuation { continuation in
DispatchQueue.global().async {
do {
let data = try Data(contentsOf: url)
if let image = UIImage(data: data) {
continuation.resume(returning: image)
} else {
continuation.resume(throwing: ImageError.invalidData)
}
} catch {
continuation.resume(throwing: error)
}
}
}
}
// ์ฌ์ฉ
Task {
do {
let image = try await downloadImage(from: url)
imageView.image = image
} catch {
print("์๋ฌ: \(error)")
}
}
async/await๋ ์ฝ๋๋ฅผ ์์ฐจ์ ์ผ๋ก ์ฝ์ ์ ์๊ฒ ํด์ค์ ๊ฐ๋
์ฑ์ด ํจ์ฌ ์ข์.
ํ์ง๋ง GCD์ OperationQueue์ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ๋ค(QoS, ์์กด์ฑ ๊ด๋ฆฌ ๋ฑ)์ ์ฌ์ ํ ์ ์ฉํ๋๊น
์ํฉ์ ๋ง๊ฒ ์กฐํฉํด์ ์ฌ์ฉํ๋ฉด ์ต๊ณ ์ผ! ๐ฏ
์ค์ ํ๋ก์ ํธ ์์ด๋์ด ๐ก
๋ฐฐ์ด ๋ด์ฉ์ ์ค์ตํด๋ณผ ์ ์๋ ํ๋ก์ ํธ ์์ด๋์ด๋ฅผ ๋ช ๊ฐ์ง ์ ์ํ ๊ฒ.
ํ๋ก์ ํธ 1: ์ด๋ฏธ์ง ํํฐ ์ฑ
์ฌ๋ฌ ํํฐ๋ฅผ ๋์์ ์ ์ฉํด์ ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋ฅผ ๋ณด์ฌ์ฃผ๋ ์ฑ.
์ฌ์ฉ ๊ธฐ์ :
โข OperationQueue๋ก ํํฐ ์์
๊ด๋ฆฌ
โข ์์กด์ฑ์ผ๋ก ์์ฐจ ์ฒ๋ฆฌ
โข ์ทจ์ ๊ธฐ๋ฅ์ผ๋ก ๋ถํ์ํ ์์
์ค๋จ
ํ๋ก์ ํธ 2: ํ์ผ ๋ค์ด๋ก๋
์ฌ๋ฌ ํ์ผ์ ๋์์ ๋ค์ด๋ก๋ํ๊ณ ์งํ๋ฅ ์ ํ์ํ๋ ์ฑ.
์ฌ์ฉ ๊ธฐ์ :
โข GCD๋ก ๋์ ๋ค์ด๋ก๋
โข DispatchGroup์ผ๋ก ์๋ฃ ๊ฐ์ง
โข ๋ฉ์ธ ์ค๋ ๋์์ UI ์
๋ฐ์ดํธ
ํ๋ก์ ํธ 3: ๋ฐ์ดํฐ ๋๊ธฐํ ์ฑ
๋ก์ปฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์๋ฒ๋ฅผ ๋๊ธฐํํ๋ ์ฑ.
์ฌ์ฉ ๊ธฐ์ :
โข Serial Queue๋ก ๋ฐ์ดํฐ ๋ฌด๊ฒฐ์ฑ ๋ณด์ฅ
โข OperationQueue๋ก ๋ณต์กํ ๋๊ธฐํ ๋ก์ง
โข ์ฐ์ ์์๋ก ์ค์ํ ๋ฐ์ดํฐ ๋จผ์ ์ฒ๋ฆฌ
๋ง๋ฌด๋ฆฌํ๋ฉฐ ๐
์, ์ฌ๊ธฐ๊น์ง ์ค๋๋ผ ์๊ณ ํ์ด! ๋ฉํฐ์ค๋ ๋ฉ์ ์ฒ์์ ์ด๋ ต๊ฒ ๋๊ปด์ง ์ ์์ง๋ง,
๊ณ์ ์ฐ์ตํ๋ค ๋ณด๋ฉด ์์ฐ์ค๋ฝ๊ฒ ์์ ์ต๊ฒ ๋ผ. ๐ฏ
ํต์ฌ ์ ๋ฆฌ ๐
GCD (Grand Central Dispatch)
โข ๊ฐ๋จํ๊ณ ๋น ๋ฅธ ๋น๋๊ธฐ ์ฒ๋ฆฌ
โข ํด๋ก์ ๊ธฐ๋ฐ์ ์ง๊ด์ ์ธ API
โข ์์คํ
๋ ๋ฒจ ์ต์ ํ
โข ๊ฐ๋ฒผ์ด ์์
์ ์ต์
OperationQueue
โข ๊ฐ์ฒด ์งํฅ์ ์ ๊ทผ
โข ์์กด์ฑ ๊ด๋ฆฌ ๊ธฐ๋ฅ
โข ์์
์ทจ์ ๋ฐ ์ฐ์ ์์ ์ค์
โข ๋ณต์กํ ์์
ํ๋ฆ์ ์ต์
๊ณตํต ์ฃผ์์ฌํญ
โข UI๋ ํญ์ ๋ฉ์ธ ์ค๋ ๋์์
โข Race Condition ์กฐ์ฌ
โข Strong Reference Cycle ๋ฐฉ์ง
โข ์ ์ ํ QoS ์ ํ
๋ฉํฐ์ค๋ ๋ฉ์ ํ๋ ์ฑ ๊ฐ๋ฐ์์ ํ์์ ์ธ ๊ธฐ์ ์ด์ผ.
์ฌ์ฉ์๋ค์ ๋ถ๋๋ฝ๊ณ ๋ฐ์์ฑ ์ข์ ์ฑ์ ์ํ๊ฑฐ๋ . ๐ฑ
์ค๋ ๋ฐฐ์ด ๋ด์ฉ์ ๋ฐํ์ผ๋ก ๋ฉ์ง ์ฑ์ ๋ง๋ค์ด๋ด!
์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์ ๋ค๋ฅธ ๊ฐ๋ฐ์๋ค๊ณผ ์ง์์ ๋๋๋ ๊ฒ๋ ์ข์ ํ์ต ๋ฐฉ๋ฒ์ด์ผ.
์ง๋ฌธํ๊ณ , ๋ต๋ณํ๊ณ , ์ฝ๋๋ฅผ ๊ณต์ ํ๋ฉด์ ํจ๊ป ์ฑ์ฅํ ์ ์๊ฑฐ๋ . ๐ฑ
ํน์ ๋งํ๋ ๋ถ๋ถ์ด ์์ผ๋ฉด ์ฃผ์ ํ์ง ๋ง๊ณ ์ปค๋ฎค๋ํฐ์ ๋ฌผ์ด๋ด. ๋ค๋ค ๊ธฐ๊บผ์ด ๋์์ค ๊ฑฐ์ผ!
๋ฉํฐ์ค๋ ๋ฉ ๋ง์คํฐ๊ฐ ๋๋ ๊ทธ๋ ๊น์ง, ํ์ดํ
! ๐ชโจ
๋ฒ๊ทธ ์๋ ์ฝ๋, ๋ถ๋๋ฌ์ด ์ฑ์ ๋ง๋๋ ๊ฐ๋ฐ์๊ฐ ๋๊ธธ ์์ํ ๊ฒ! ๐
๊ด๋ จ ํค์๋
๋๊ธ ์์ฑ
์ด ๊ธ์ ๋ํ ์ฌ๋ฌ๋ถ์ ์๊ฐ์ ๋ค๋ ค์ฃผ์ธ์
๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค
๋๊ธ์ ์์ฑํ๋ ค๋ฉด ๋จผ์ ๋ก๊ทธ์ธํด์ฃผ์ธ์.
๋๊ธ 0
์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.
์์ง ๋๊ธ์ด ์์ต๋๋ค.