Combine을 공부하다가 주로 receive(on: )
을 통해 메인 스레드 혹은 Runloop.main으로 스레드를 변경할 때가 있습니다.
어떤 차이점이 있는지 궁금해서 알아보았습니다.
번역을 통해 진행하였습니다.
https://www.avanderlee.com/combine/runloop-main-vs-dispatchqueue-main/
Combine scheduler란 클로저가 언제, 어떻게 실행될지를 정의하는 것이다.
Scheduler들은 Scheduler
프로토콜을 따르고 있고, RunLoop.main과 DispatchQueue.main 또한 따르고 있다.
가장 흔히 사용되는 예시는 receive(on: option:)
으로 Combine stream을 설정(setting up)할 때 사용된다.
URLSession.shared
.dataTaskPublisher(for: URL(string: "https://picsum.photos/300/600")!)
.map(\.data)
.compactMap(UIImage.init)
/// Schedule to receive the sink closure on the
/// main dispatch queue.
.receive(on: DispatchQueue.main, options: nil)
.sink { _ in
print("Image loading completed")
} receiveValue: { image in
self.image = image
}.store(in: &cancellables
위 코드에서 확인할 수 있듯이, Image를 할당하는 것을 메인스레드에서 하도록 만들 수 있다.
main queue scheduler는 main thread에서 UI업데이트 하도록 할 때 요구된다.
RunLoop은 Application의 touch와 같은 input source들을 다루는 object의 인터페이스이다.
RunLoop은 각 쓰레드의 object를 위해 RunLoop object를 생성하는 시스템에 의해 생성되고 관리된다.
메인 스레드를 나타내는 Main Run Loop도 생성한다.
DispatchQueue.main은 현재 프로세스의 메인스레드와 관련이 있는 dispatch Queue이다.
DispatchQueue는 관련된 스레드에서 직렬 혹은 병렬로 작업을 실행한다.
RunLoop.main과 DispatchQueue.main은 메인 스레드에서 UI를 업데이트 하는데 모두 사용된다.
둘 다 Combine value가 publishe된 이후에 UI 업데이트를 하도록 해준다.
가장 큰 차이점은 DispatchQueue는 즉시 실행되는 것이고 반면에 RunLoop은 busy할지도 모른다.
만약 DispatchQueue.main
을 스케줄러로 사용할 때는, 스크롤하면서 동시에 UI가 업데이트 된다.
반면에 RunLoop.main
은 스크롤이 마친 이후에 UI가 업데이트 된다.
즉, main run loop에 의해 scheduled된 클로저는 유저 Interation이 발생하면 즉시 실행되지 않고 끝나야 실행된다.
main run loop은 여러개의 모드를 사용한다. 아래중에서 iOS는 common, default, traking 모드만 지원을 한다.
유저 Interation이 발생하면 non-default모드로 변경된다. 그러나 RunLoop.main은 오직 default모드에서만 실행(active)된다. 달리 말하면, User interation이 끝날 때 Default Mode로 바뀌게 되고 closure가 실행된다.
Default로 DispatchQueue.main을 사용하면 될 것 같다.
UI를 업데이트하기 전에 처리해야할 User Interation이 있다면 RunLoop.main
스크롤하는동안 UI를 업데이트하면 Frame per second(FPS)에 영향을 주고 스크롤하는데 영향을 줄 것이다.
스크롤 할 때 UI업데이트가 필요하지 않을 수도 있다. 이경우엔 RunLoop.main