์ด์ ๊ธ์์ RunLoop์ ๋ํด ์์๋ณด์๋ค. Timer๋ ๊ฐ์ด ์ฒ๋ฆฌํ๋ค ํ์๋๋ฐ, ์ด๋ฒ์๋ Timer์ ๋ํด์ ์์๋ณด๋ คํ๋ค.
Timer
ํน์ ์๊ฐ ๊ฐ๊ฒฉ์ด ์ง๋ ํ, Target ๊ฐ์ฒด๋ก ๋ฉ์์ง๋ฅผ ์ ์กํ๋ค.
๋ฌธ์๋ฅผ ์ฝ์ด๋ณด๋, Run loop๋ Timer์ ๋ํด ๊ฐํ ์ฐธ์กฐ๋ฅผ ์ ์งํ๊ธฐ ๋๋ฌธ์, Run loop์ ํ์ด๋จธ๋ฅผ ์ถ๊ฐํ ํ์ ๊ฐํ ์ฐธ์กฐ๋ฅผ ์ ์งํ์ง ์์๋ ๋๋ค๊ณ ํ๋ค. ์ด๊ฒ ๋ฌด์จ๋ง์ธ๊ฐ ์ถ์ด ์ข ์ฐพ์๋ณด๋, run loop์ timer๋ฅผ ์ถ๊ฐํ ์ ์๋๋ฐ ๊ทธ ๋ค๋ก๋ timer ๊ฐ์ฒด๋ฅผ ๋ค๊ณ ์์ ํ์๊ฐ ์๋ค๋ผ๋ ๋ง์ธ ๊ฒ ๊ฐ๋ค. ํ์ง๋ง fire๋ฅผ ์ํด์๋ ๋ค๊ณ ์๋ ๊ฒ์ด ์ข์ ์๋ ์๋ค.
๊ทธ ๋ค์์ผ๋ก ๋ฌธ์์์๋ Timer๊ฐ ์ค์๊ฐ ๋งค์ปค๋์ฆ์ด ์๋๋ผ๊ณ ํ๋ค. ์ด๋ Run loop์ ๋ํด ์์์ผ ํ๋ค. ์ด์ ๊ธ์ ์ฐธ๊ณ ํ์.
์ผ๋จ ์๊ฐํด๋ณด๋ฉด, Timer๋ Run loop์์์ ๋์๊ฐ๋ ๋ ์์ด๋ค. ๊ทธ๋ฐ๋ฐ run loop๋ input source์ญ์ ์ฒ๋ฆฌํ๋ค. ๋ง์ฝ ๋ด๊ฐ timer๋ฅผ main thread์์ ์ฒ๋ฆฌํ๋๋ก ํ ๊ฒฝ์ฐ์ธ๋ฐ, input source๊ฐ ๊ต์ฅํ ๋ง์ด ๋ค์ด์จ๋ค. ๊ทธ๋ฌ๋ฉด ๋จผ์ ์์ฌ์๋ ์ด ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ๋ ค๊ณ ํ ๊ฒ์ด๋ค. ๊ทธ๋ฐ๋ฐ ๊ทธ์์ค์ ๊ฐ์๊ธฐ timer๊ฐ fire๋์ด์ผ ํ๋ ์๊ธฐ๊ฐ ์ฐพ์์๋ค. ํ์ง๋ง ์์ ์์ธ event๊ฐ ๋ง๊ธฐ ๋๋ฌธ์ ์ค์ ๋ก ์ํ๋ time interval์ด ์ง๋ ํ์ timer๊ฐ fire๋๋ค. ์ด๋ฌํ ์ ์์ ์ค์๊ฐ ๋งค์ปค๋์ฆ์ด ์๋๋ผ๋ ๋ง์ ํ๋ ๊ฒ์ด๋ค. ์ดํ ์์๋ค์์ ๋ณด๋ฉด ์๊ฒ ์ง๋ง, ๋ณดํต timer๋ฅผ ์ฌ์ฉํ ๋ ์ ์ธํ๋ ๋ฐฉ์์ ์์์ ์ผ๋ก main thread์์ ์ฒ๋ฆฌ๋๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ์ด๋ ๊ฒ real time์ด ์๋ ์ ์๋ค๋ ์ ์ ์๋ ๊ฒ์ ์ค์ํ ์ ์๋ค. (๊ทธ๋ ๋ค๋ฉด ๋ค๋ฅธ run loop์ timer๋ฅผ ๋ฃ์ ์ ์๋ค๋ ์๊ธฐ์ธ๊ฐ?: ๊ทธ๋ ๋ค) ์ด๋ ๊ฒ timer๊ฐ fire๋๋ ์๊ฐ ๊ฐ๊ฒฉ์ Timer Tolerance๋ผ ํ๋ค.
Timer ๊ฐ์ฒด๋ CFRunLoopTimer์ ์ฐ๊ฒฐ๋๋ค๊ณ ํ๋ค. ์ ํํ์ง๋ ์์ผ๋, ์ด๋ ๊ฒ Type casting์ ํ๋๋ฐ ์์ด cost๊ฐ ์์ด ์์ ์ ํ ์ ์๋ ๊ฐ๋ ์ Toll-Free Bridging์ด๋ผ ๋ถ๋ฅธ๋ค๊ณ ํ๋ค. Objective C์ ๊ด๋ จ๋ ๊ฐ๋ ์ด๋ผ ์ผ๋จ ๋์ด๊ฐ๋ค.
Comparing Repeating and Nonrepeating Timers
์ผ๋จ ํ์ด๋จธ์์ ๊ฐ์ฅ ํฐ ๋ถ๋ฅ๋ ์ด๊ฒ์ผ๋ก ๋งํ ์ ์๋ค. ๋ฐ๋ณตํ๋๋, ๋ฐ๋ณตํ์ง ์๋๋.
Repeating Timers
๋ฐ๋ณต ํ์ด๋จธ์ ๊ฒฝ์ฐ ํน์ ๋ฐ๋ณต์๊ฐ์ ๊ธฐ์ค์ผ๋ก ์ค์ผ์ค๋ง ๋๋ค. 5์ด๋ง๋ค fire๋ฅผ ์ํ๋ ๊ฒฝ์ฐ, ์ผ๋จ ์์ ๋ fire ์ผ์ ์ 5์ด๊ฐ๊ฒฉ์ผ๋ก ์กํ๊ฒ ๋๋ค. ๊ทธ๋ฐ๋ฐ ํด๋น run loop์ ๋ค๋ฅธ event๋ค์ด ๋ง์ด ์์ฌ ์๋ ๊ฒฝ์ฐ ์ด๋์ ๋๋ delay๋๋ค. ๊ทธ delay ์์ค์ด time interval๋ณด๋ค ๋์ด๊ฐ ๊ฒฝ์ฐ์๋ ๊ทธ ์๊ฐ๋ํ ํ๋ฒ๋ง fire๋๋ค.
let timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(fireTimer), userInfo: nil, repeats: true)
@objc func fireTimer() {
print("Timer fired!")
}
์ ๋ฐฉ์์ @objc
๋ฅผ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ Target/Action ๋ฐฉ์์ ํ์ฉํ ๊ฒ์ด๋ค. ๋ง์ฝ objective c ๋ฐฉ์์ด ์๋๋ผ๋ฉด ๋ค์๊ณผ ๊ฐ์ด ์ฌ์ฉํ ์ ์๋ค.
let timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
print("Timer fired!")
}
ํด๋น ํจ์๋ฅผ ์ฌ์ฉํ ๊ฒฝ์ฐ return value๋ก Timer๋ฅผ ๋ฐํํ๋ค. ๋ฐ๋ณตํ๋ ๊ฒฝ์ฐ ํด๋น ๊ฐ์ฒด๋ฅผ ์ ์ฅํด๋์ด์ผ ์ถํ invalidate()
๋ฅผ ํ ์ ์๊ธฐ ๋๋ฌธ์ ๊ฐ์ง๊ณ ์๋ ๊ฒ์ด ์ข์ ์ ์๋ค. ํน์ closure๊ฐ ํธ์ถ๋๋ ์์ ์ invalidateํ๋ ๊ฒ๋ ๊ฐ๋ฅํ๋ค.
Nonrepeating Timers
ํ๋ฒ fireํ์ ์๋์ผ๋ก ๋ฌดํจํ ๋๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ๋ด๊ฐ ํ๋ฒ๋ง ์ฌ์ฉํ ๊ฒ์ด๋ผ๋ฉด ์ด๋ ์์ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข๋ค.
let timer1 = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(fireTimer), userInfo: nil, repeats: false)
let timer2 = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: false) { timer in
print("Timer fired!")
}
์ด๋ฐ์์ผ๋ก argument์ ์ถ๊ฐํ์ฌ ์ฌ์ฉํ ์ ์๋ค. ๊ทธ๋ฐ๋ฐ ๊ตณ์ด ํ๋ฒ๋ง ์ฌ์ฉํ๋ค๋ฉด ์ด๋ ๊ฒ ํ ํ์๊ฐ ์๋ค.
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
print("Timer fired!")
}
Ending the timer
์๊น๋ ๋งํ์ง๋ง, closure๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ timer ๊ฐ์ฒด๋ฅผ ์ฃผ๊ธฐ ๋๋ฌธ์, ๊ทธ ์์์ ์ข
๋ฃ ์ฒ๋ฆฌ๋ฅผ ํ ์ ์๋ค. guard
๋ฌธ ๊ฐ์ ๊ฑธ๋ก ealry exit๋ฅผ ์ฒ๋ฆฌํ ์๋ ์๊ฒ ๋ค.
var runCount = 0
Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
print("Timer fired!")
runCount += 1
if runCount == 3 {
timer.invalidate()
}
}
Adding Context
์ด๊ฑด Target/Action ๋ฐฉ์์ ์ฌ์ฉํ ๊ฒฝ์ฐ, ์ฆ, Objective c runtime์์ ์ฒ๋ฆฌํ ๊ฒฝ์ฐ์ ์ฌ์ฉํ ์ ์๋ ํจ์์ด๋ค. Timer๊ฐ ๋ฐ๋๋์ด ํน์ ํจ์๋ฅผ ์คํ์๊ธธ ๋, Timer ๊ฐ์ฒด๊ฐ ๊ฐ์ด ๋์ด๊ฐ๊ฒ ๋๋๋ฐ, ๊ฑฐ๊ธฐ์ ์ํ๋ context ์ ๋ณด๋ฅผ ๋ฃ์ด์ ์ฒ๋ฆฌํ ์ ์๋ค.
let context = ["user": "@wansook"]
Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(fireTimer), userInfo: context, repeats: true)
@objc func fireTimer(timer: Timer) {
guard let context = timer.userInfo as? [String: String] else { return }
let user = context["user", default: "Anonymous"]
print("Timer fired by \(user)!")
runCount += 1
if runCount == 3 {
timer.invalidate()
}
}
Timer Tolerance
Timer๋ ๊ธฐ๋ณธ์ ์ผ๋ก runloop์์์ ๋์๊ฐ๊ธฐ ๋๋ฌธ์, event์ ์์ฌ์๋ ์ ๋์ ๋ฐ๋ผ ์คํ๋๋ ์๊ฐ์ ์ํฅ์ ๋ฏธ์น๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ system์ ์ฅ์์๋ ์ด ์๊ฐ์ ์์งํค๊ธฐ ์ํด ์ค์ผ์ฅด๋ง์ ์ํด์ผ ํ๋๋ฐ, ์ด๊ฒ cost๊ฐ ๋ค์ด๊ฐ๋ ์์ ์ผ ์ ๋ฐ์ ์๋ค.
์ด๋ฌํ ์ ์์ Apple์ ์ฝ๊ฐ์ ์ ๋๋ฆฌ๋ฅผ ๋ถ์ฌํด์ค๋ค๋ฉด ์ข ๋ ์ ๋ ฅ ์ฌ์ฉ๋์ ๋์์ด ๋ ๊ฑฐ๋ผ๊ณ ํ๋ค. ๊ทธ๊ฒ ๋ฐ๋ก ์ด ๊ฐ๋ ์ด๋ค.
let timer = Timer.scheduledTimer(timeInterval: 5.0, target: self, selector: #selector(fireTimer), userInfo: nil, repeats: true)
timer.tolerance = 0.5
5์ด ๋ฐ๋ณต timer๋ฅผ ๋ง๋ค์๋ค๊ณ ๊ฐ์ ํด๋ณด์. ๊ธฐ๋ณธ์ ์ผ๋ก๋ tolerance๊ฐ 0์ด๊ธฐ ๋๋ฌธ์ system์ ์ต๋ํ ์ด ๊ธฐ์ค์ ๋ง์ถ๋ ค๊ณ ๋ ธ๋ ฅํ๋ค. ๊ทธ๋์ ํ๋ค์ดํ๋ค. ์ฌ๊ธฐ์ ๋ด๊ฐ tolerance๋ฅผ 0.5์ด๋ก ์ง์ ํด์ฃผ๋ฉด ์ฝ๊ฐ์ ์จํต์ด ํธ์ธ๋ค. ์ผ๋จ์ ์ต๋ํ tolerance๊ฐ 0์ ๋ง์ถ๋ ๊ฑธ ๋ ธ๋ ฅํ๋, ์ด์ฉ ์ ์๋ ์์ ์์๋ tolerance๋ฅผ ๋ณด๊ณ ์ ๋๋ฆฌ์๊ฒ ์ฒ๋ฆฌํ๋ค.
์ฌ๊ธฐ์ ๋งน์ ์ ์คํ์๊ฐ์ด ๋ฆ์ถฐ์ก๋ค๊ณ ํด์ ๋ค์ ์คํ์๊ฐ์ด ๋ฆ์ถฐ์ง๋ ๊ฒ์ ์๋๋ผ๋ ์ ์ด๋ค. ์ ๋๋ฆฌ์๊ฒ ์ฒ๋ฆฌํ๋ค๋ ๊ฒ ๋ฑ ๋ง๋ ์ค๋ช ์ด๋ค.
Working with RunLoop
Tolerance๊น์ง ์ดํดํ๋ค๋ฉด ํ๊ฐ์ง ์๋ฌธ์ ์ด ๋ค ์ ์๋ค. ๊ทธ๋ผ input source๊ฐ ๊ณ์ํด์ ๋ค์ด์ค๋ฉด timer๊ฐ ์๋ํ์ง ์์ ์๋ ์๋๋ฐ, ์ด๊ฑฐ ๋ฌธ์ ์๋๊ฐ? ์ฌ์ฉ์ ๊ฒฝํ์ ํด์น๋ ๊ฒ ์๋๊ฐ??
๋ง๋ค. ๊ทธ๋ฐ ๊ฒฝ์ฐ๊ฐ ์์ ์ ์๋ค. ์ผ๋จ Timer ๊ฐ์ฒด๋ ๊ธฐ๋ณธ์ ์ผ๋ก ์ฐ๋ฆฌ๊ฐ ์ฌ์ฉํ๋ ๋ฐฉ์์ด Main Thread์ ๋ฃ๋ ๋ฐฉ์์ด๋ค. ๊ทธ๋ ๊ฒ ๋๋ ์ฌ์ฉ์์ interaction์ด ๋ง์ด ๋ค์ด์ค๋ฉด timer๊ฐ ๋ฐ๋๋์ง ์๋๋ค. ๋ํ์ ์ผ๋ก Scroll์ด ์๊ฒ ๋ค. ์์ ๋๊ณ ์ฒ์ฒํ ๊ณ์ํด์ ์์ง์ธ๋ค๋ฉด system์ ๊ณ์ํด์ event๋ฅผ ๋ด๋ณด๋ผ ๊ฑฐ๊ณ , ๊ทธ๊ฑธ ์ฒ๋ฆฌํ๋ ๊ณณ์ main runloop์ด๋ค. ๊ทธ๋ฌ๋ฉด timer๊ฐ ๋ฐ๋๋์ด์ผ ํ๋ ์์ ์ ๋ฐ๋์ด ๋ชป๋๋ ๊ฒฝ์ฐ๋ ์์ ์ ์๋ค.
์ด๋ฐ ๊ฒฝ์ฐ ์ฐ๋ฆฌ๊ฐ ์ ํํ ์ ์๋ ๋ฐฉ๋ฒ์ ๋ด๊ฐ ์ํ๋ runloop์์ timer๋ฅผ ๋ฐ๋์ํค๋ ๊ฒ์ด๋ค.
let timer = Timer(timeInterval: 1.0, target: self, selector: #selector(fireTimer), repeats: true)
RunLoop.current.add(timer, forMode: .common)
์ด ๊ฐ๋ ์ ๋ฐ๋ณตํ์ง ์๋ timer์๋ ์ ์ฉํ ์ ์๋๋ฐ, ์ด ๊ฒฝ์ฐ์๋ DispatchQueue๋ฅผ global queue๋ก ๋ฐ๊ฟ์ ์คํ์ํค๋ ๊ฒ์ด ๋ณด๋ค ๋์ ๋ฐฉ๋ฒ์ด๋ผ ์๊ฐ๋๋ค.
DispatchQueue.global.asyncAfter(deadline: .now() + 1) {
print("Timer fired!")
}
๋ง๋ฌด๋ฆฌ
์ด๋ ๊ฒ Timer์ ๋ํด์ ์ ๋ฆฌํด๋ณด์๋ค. Cheeting note๊ฐ ๋ ๋ฏํ๋ค. ๋!