Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- 애니메이션
- Refactoring
- Xcode
- collectionview
- SWIFT
- tableView
- clean architecture
- 클린 코드
- swift documentation
- 리펙토링
- Protocol
- swiftUI
- ios
- 리펙터링
- combine
- map
- Human interface guide
- uiscrollview
- ribs
- uitableview
- HIG
- RxCocoa
- rxswift
- UICollectionView
- 스위프트
- Clean Code
- MVVM
- 리팩토링
- Observable
- UITextView
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - swift] Run Loops (런 루프, Thread 프로그래밍, global queue에서 Timer 동작 방법) 본문
iOS 응용 (swift)
[iOS - swift] Run Loops (런 루프, Thread 프로그래밍, global queue에서 Timer 동작 방법)
jake-kim 2021. 6. 3. 23:15
- 특정 이벤트가 왔을 때 쓰레드가 일해야 할 때는 일하고, 일이 없으면 쉬도록하기 위해 애플에서 만든 쓰레드관리 Loop
- 이벤트가 들어오면 이벤트 헨들러를 실행하는데, 이벤트 핸들러를 언제 실행하지 결정해주는 루프를 의미
- Run Loop는 두 가지의 소스를 수신
- 1) input source: 한 루프 한바퀴를 도는 동안, 다른 스레드나 다른 응용프로그램의 비동기 이벤트가 수신된 것을 확인하고, 이벤트 핸들러 수행
- 2) timer source: 한 루프 한바퀴를 도는 동안, 예정된 시간이나 반복되는 간격으로 발생하는 동기 이벤트를 수신된 것을 확인하고, 이벤트 핸들러 수행
- Thread를 생성하고 Thread.current에 접근할 때 Run Loop가 없다면 생성하고, 있으면 기존에 있으면 그것을 사용
- Run Loop의 실행은 개발자가 직접 실행
- 특정 Thread가 input source나 timer source를 처리해야 하는 경우, RunLoop에 직접 접근하여 실행해야 가능
Run Loops와 Thread
- Thread는 모두 각자의 Run Loop를 소유
- Thread를 생성하고 Thread.current에 접근할 때 Run Loop가 없다면 생성
- 주의: Run Loop는 자동으로 실행되지 않는 형태 -> Run Loop 실행하기 위해서는 프로그래머가 직접 호출
Main과 Global Thread에서의 Run Loop
- Main thread에서는 Run Loop가 자동으로 설정되고 실행 (Main Run Loop)
- Global thread에서는 Run Loop를 프로그래머가 직접 실행해야 동작
cf) Main queue vs Global queue
main queue | global queue | |
갯수 | 한 개 | 여러 개 |
serial | concurrent | |
스레드 | main thread | QoS에 따른 queue 생성 |
DispatchQueue.main.async{} | DispachQueue.global() DispachQueue.global(qos:) |
- 즉 GCD(Global Central Dispatch)를 통해 Global thread를 사용할 때 Run Loop를 프로그래머가 실행시켜주지 않으면 동작되지 않는 경우가 존재
- ex) Timer 설정, Socket Input, touch input 등
// Timer 동작 안하는 경우가 존재
// global thread - 동작 x
DispatchQueue.global().async {
Timer.scheduledTimer(withTimeInterval: 3, repeats: false) { _ in
print("타이머 동작!")
}
}
// Main Thread - 동작 o
Timer.scheduledTimer(withTimeInterval: 3, repeats: false) { _ in
print("타이머 동작!")
}
Run Loop 실행 방법
- Run Loop 객체 참조 방법: 생성된 queue안에서 객체를 획득하여 사용 (RunLoop는 자동으로 생성되므로 참조하여 바로 사용)
let runLoop = RunLoop.current
- 실행 방법 - 4가지 메서드로 실행
- run(): Input sources, Timer sources들을 영구적으로 처리
DispatchQueue.global().async {
Timer.scheduledTimer(withTimeInterval: 3, repeats: true) { _ in
print("타이머동작!")
}
RunLoop.current.run()
}
* 주의) Timer Sources 실행 전 run()을 호출하면 동작하지 않는것을 주의: 이벤트가 발생한 후 run()을 통해 loop로 받아야 하므로
// 동작 x
DispatchQueue.global().async {
RunLoop.current.run()
Timer.scheduledTimer(withTimeInterval: 3, repeats: true) { _ in
print("타이머동작!")
}
}
- run(until:): 지정된 date까지 loop실행, 이 메소드를 가장 많이 사용
- run()은 영구적으로 실행되므로 기한을 정해주어 자원 낭비를 줄일 수 있는 run(until:) 지향
ex) 간단한 예제 - isRunning을 false로 해주면 멈추게끔
* 만약 sync하게 동작할 경우 while문에서 무한 loop가 진행되므로, 실제로 구현 시 running 작업을 또 다른 thread로 구분하여 설계
DispatchQueue.global().async {
let isRunning = true
let runLoop = RunLoop.current
Timer.scheduledTimer(withTimeInterval: 3, repeats: true) { _ in
print("타이머동작!")
}
while isRunning {
runLoop.run(until: Date().addingTimeInterval(0.1))
}
}
ex) socket 통신 - run loop이 sync에서 짜여있어도 최종적으로 async에서 work를 부르는 형태로 동작
var workItemRunLoop: DispatchWorkItem?
func runLoopSample() {
workItemRunLoop = DispatchWorkItem {
let runLoop = RunLoop.current
let inputStream = InputStream()
let outputStream = OutputStream()
inputStream.schedule(in: runLoop, forMode: .default)
outputStream.schedule(in: runLoop, forMode: .default)
while !(self.workItemRunLoop?.isCancelled ?? true) {
runLoop.run(until: Date().addingTimeInterval(0.1))
}
}
guard let workItemRunLoop = workItemRunLoop else { return }
DispatchQueue.global().async(execute: workItemRunLoop)
}
Mode를 필요로 하는 run
- run(mope:before:): loop를 한번 실행하며, 지정된 mode와 date까지 input을 blocking
- acceptInput(forMode:before:): loop를 한 번 실행하며, 지정된 모드에서만 입력을 허용
- mode의 개념: Input sources
모드 | 이름 | 설명 |
Default | RunLoop.Mode.default | NSConnection객체르 제외한 input sources 처리 모드 |
Tracking | RunLoop.Mode.tracking | 컨트롤을 추적하고 있는 상황에서 사용 |
Common | RunLoop.Mode.common | -일반적으로 사용되는 모드에 같이 사용가능한 그룹 -Input Source를 이 모드와 연결하여 각 모드와 연결하여 사용 |
RunLoop 사용처
- global queue에서 Input Source를 통해서 다른 thread와 통신
- global queue에서 Timer 사용
- global queue에서 주기적인 일을 계속 수행하는 경우
* 참고
https://developer.apple.com/documentation/foundation/runloop/run_loop_modes
https://developer.apple.com/documentation/foundation/runloop
'iOS 응용 (swift)' 카테고리의 다른 글
Comments