비동기 프로그래밍에서 UI 업데이트를 진행해야 할 때는 반드시 메인 스레드에서 동작하도록 설정해줘야 한다.
Combine의 경우 receive(on:) 메서드에 스케줄러를 설정해 주면 된다.
이때 RunLoop.main과 DispatchQueue.main을 통해 메인 스레드 지정이 가능한데, 둘의 차이점에 대해 궁금해져서 구글링을 해보았다.
Combine의 스케줄러는 언제, 어떻게 클로저를 실행시킬 지 정의한다.
스케줄러들은 Scheduler 프로토콜에 순응하며, RunLoop.main과 DispatchQueue.main도 이 프로토콜을 채택하고 있다.
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)
RunLoop는 어플리케이션에 대한 터치와 같은 입력 소스를 관리하는 객체에 대한 인터페이스이다.
시스템은 RunLoop를 생성하고 관리하며, 각 스레드 객체에 대한 RunLoop 객체를 생성하는 역할도 한다.
또한 시스템은 메인 스레드를 나타내는 메인 RunLoop를 생성하는 역할도 한다.
DispatchQueue.main은 현재 프로세스의 메인 스레드와 연결된 DispatchQueue이다.
시스템은 메인 스레드를 나타내는 이 DispatchQueue를 생성하는 역할을 담당한다.
DispatchQueue는 연결된 스레드에서 연속적으로 또는 동시에 작업을 실행한다.
DispatchQueue.main과 RunLoop.main은 메인 스레드에서 동작한다는 공통점을 가지고 있다.
즉, 둘 다 UI를 업데이트하는 목적으로 사용된다.
가장 중요한 차이는 DispatchQueue.main은 즉시 실행되지만, RunLoop는 busy할 수도 있어 즉시 실행되지 않을 수 있다는 것이다.
사용자가 스크롤하고 있는 상황을 가정했을 때, DispatchQueue.main은 스크롤과 동시에 UI가 업데이트되는 반면, RunLoop.main은 스크롤이 끝난 이후에 UI가 업데이트된다. 즉, RunLoop.main에 스케줄 된 클로저는 사용자 인터랙션이 발생할 때마다 실행이 지연되게 된다.
이는 RunLoop.main이 여러 모드들을 사용하며, 사용자 상호작용이 발생하면 default 모드가 아닌 모드로 전환이 되는데, Combine scheduler는 오직 default 모드가 활성일 때만 실행되기 때문이다. 사용자 인터랙션이 종료되고 Combine의 클로저가 실행되면 다시 default 모드로 전환된다.
RunLoop에 대해 개념이 잡히지 않은 상태로 구글링해서 그런지 모르는 부분이 많다. 빠른 시일내로 공부해봐야 할 것 같다...^^
https://www.avanderlee.com/combine/runloop-main-vs-dispatchqueue-main/
RunLoop.main vs DispatchQueue.main: The differences explained
RunLoop.main and DispatchQueue.main are often used as Combine schedulers to update the user interface. Though, both behave very differently.
www.avanderlee.com
throttle (0) | 2024.02.29 |
---|---|
Combine은 withLatestFrom을 제공하지 않는다 (0) | 2023.12.25 |
Combine과 함께 NotificationCenter 사용해보기 (0) | 2023.12.10 |