RunLoop?
- 입력 소스를 관리하는 객체들에 대한 programmatic 인터페이스
- RunLoop 객체는 마우스와 키보드 이벤트 등의 소스들로부터의 입력을 처리한다.
- RunLoop 객체는 Timer 이벤트를 처리하기도 함
- RunLoop는 앱이 명시적으로 생성 및 관리하지는 않음.
- 시스템이 각 Thread 객체(앱의 메인 스레드 포함)마다 필요에 따라 RunLoop 객체를 생성한다.
- 현재 스레드의 RunLoop에 접근해야 한다면 클래스 메서드 current를 활용!
- RunLoop 클래스는 스레드 안전하지 않기 때문에 다른 스레드에서 실행 중인 RunLoop 객체의 메소드를 호출하면 안 된다.
Main RunLoop
- 메인 스레드의 RunLoop가 Main RunLoop.
- Main RunLoop는 터치 이벤트를 받아 어떤 객체가 이벤트를 받아야 하는지 타겟을 확인하고 처리한다.

- RunLoop는 일이 없을 땐 쉬도록 설계되었기 때문에 반복 실행되지 않고 개발자가 실행해야 동작한다.
- 그러나 Main RunLoop는 시스템에 의해 자동적으로 생성 및 실행된다.
RunLoop.main vs DispatchQueue.main
- 컴바인에서 스케줄러 지정 시 어떤 스케줄러를 사용해 메인 스레드에 작업을 분배하는 것이 좋을까?
- RunLoop.main 그리고 DispatchQueue.main 둘 다 메인 스레드에서 작업을 실행한다.
- 그러나 DispatchQueue.main은 작업을 곧바로 수행하는 반면 RunLoop.main은 입력 소스를 처리하는 도중에는 작업이 곧바로 수행되지 않을 수도 있다.
- 예를 들어, 스크롤 도중 다운로드 완료된 이미지를 표시하는 작업은 DispatchQeueu.main을 스케줄러로 사용할 때에만 곧바로 수행될 것이다.
- RunLoop.main을 스케줄러로 사용할 땐 유저 입력이 수행되는 동안 이미지를 표시하는 작업이 지연된다.
URLSession.shared
.dataTaskPublisher(for: URL(string: "https://picsum.photos/300/600")!)
.map(\.data)
.compactMap(UIImage.init)
.receive(on: RunLoop.main, options: nil)
.sink { _ in
print("Image loading completed")
} receiveValue: { image in
self.image = image
}.store(in: &cancellables)
- 이는 컴바인 스케줄러로서의 RunLoop.main은 default 모드일 때만 실행되는데, 유저 상호작용이 발생하면 RunLoop.main은 non-default 모드로 변경되기 때문이다.
항상 DispatchQueue.main을 사용해야 할까?
- UI 업데이트 작업에는 기본적으로 DispatchQueue.main을 사용하는 것이 좋다.
- 그러나 스크롤 도중 UI를 업데이트하는 작업은 앱의 FPS(Frame per Second)와 부드러운 스크롤링에 영향을 줄 수 있다.
- 따라서 스크롤 도중 필수적인 UI 업데이트가 아니라면 RunLoop.main을 스케줄러로 사용해 부드러운 스크롤링 경험을 제공할 수 있다.