Combine 뽀개기 4장: Scheduler

2023. 7. 26. 20:12·iOS&Swift

Scheduler

A protocol that defines when and how to execute a closure.
클로저를 실행하는 시기와 방법을 정의하는 프로토콜입니다.

Scheduler를 사용하는 이유는 우리가 구현한 클로저가 언제 실행될지, 무슨 쓰레드에서 실행될지 제어하기 위해서이다.

때문에 Combine에선

  • DispatchQueue
  • OperationQueue
  • RunLoop
  • ImmediateScheduler

위와 같은 Scheduler를 제공한다.

위 DispatchQueue, OperationQueue를 이용하여 우린 클로저가 무슨 쓰레드에서 실행될지 제어해줄 수 있고, RunLoop 통해 어떠한 RunLoop에서 실행될 지 결정해 줄 수 있다.

ImmediateScheduler는 생소하지만, 공식문서에 의하면, 동기 작업을 수행하기 위한 스케줄러라고 나와있다. ImmediateScheduler를 사용하면 즉시 작업을 실행시킬 수 있다고 한다.

Combine은 기본적으로 Scheduler 직접 지정하지 않아도 어떠한 기준으로 Scheduler를 설정한다.

그 기준은 요소가 생성된 쓰레드이다.

var bag = Set<AnyCancellable>()

let subject = CurrentValueSubject<Int, Never>(1)

subject
    .sink { value in
        if Thread.isMainThread {
            print("작업 \(value)는 메인 쓰레드에서 실행")
        } else {
            print("작업 \(value)는 글로벌 쓰레드에서 실행")
        }
    }.store(in: &bag)


DispatchQueue.global().async {
    subject.send(2)
}
작업 1는 메인 쓰레드에서 실행
작업 2는 글로벌 쓰레드에서 실행

위와 같이 1번째 작업은 구독과 동시에 메인에서 일어났고, 2번째 작업은 DispatchQueue.global() 내부에서 일어났다.

때문에 결과를 보면, 2번째 작업에 대한 Scheduler는 자동으로 글로벌 쓰레드에서 동작하는 것을 볼 수 있다.

하지만, 요소가 어디서 생성되든 상관없이 강제로 Scheduler를 전환하는 메서드가 있다.

정확히는 메서드 다음으로 오는 클로저의 Scheduler를 변경한다.

receive(on:)

var bag = Set<AnyCancellable>()

let subject = PassthroughSubject<Int, Never>()

subject
    .map { value -> Int in
        let thered = Thread.isMainThread ? "메인" : "글로벌"
        print("작업 \(value)는 receive(on:) 전엔 \(thered)")
        return value
    }
    .receive(on: DispatchQueue.main)
    .sink { value in
        if Thread.isMainThread {
            print("작업 \(value)는 receive(on:) 후엔 메인 쓰레드에서 실행")
        } else {
            print("작업 \(value)는 receive(on:) 후엔 글로벌 쓰레드에서 실행")
        }
    }.store(in: &bag)


DispatchQueue.global().async {
    subject.send(1)
}
작업 1는 receive(on:) 전엔 글로벌
작업 1는 receive(on:) 후엔 메인 쓰레드에서 실행

결과와 같이 receive(on:) 메서드 전엔 1번째 요소가 생성된 글로벌 쓰레드 이지만, receive(on:) 메서드 후엔 메인 쓰레드로 Scheduler가 변경되었다.

이런식으로 receive(on:) 메서드가 호출된 이후의 Scheduler를 변경할 때 사용할 수 있다.

subscribe(on:)

subscribe(on:)는 구독의 Scheduler를 제어하는 메서드이다.

var bag = Set<AnyCancellable>()

let subject = CurrentValueSubject<Int, Never>(1)

subject
    .subscribe(on: DispatchQueue.global())
    .sink { value in
        if Thread.isMainThread {
            print("작업 \(value)는 메인 쓰레드에서 실행")
        } else {
            print("작업 \(value)는 글로벌 쓰레드에서 실행")
        }
    }.store(in: &bag)

subject.send(2)
작업 1는 글로벌 쓰레드에서 실행
작업 2는 메인 쓰레드에서 실행

위 결과를 보면 1번째 작업은 글로벌에서 수행됐다.

하지만, 2번째 작업은 메인에서 수행됐다.

이것이 subscribe(on:)와 receive(on:)의 차이이다.

subscribe(on:)는 단지 구독을 무슨 Scheduler에서 시킬지를 제아한다.

.subscribe(on: DispatchQueue.global())로 주었기 때문에 구독은 글로벌 쓰레드에서 일어났다.

초기값인 1번째 작업은 구독과 동시에 일어나고, 때문에 글로벌 쓰레드에서 작업이 수행된다.

하지만 2번째 작업은 메인에서 수행됐다. 이는 맨 처음에 말한 Scheduler 기본적으로 요소가 생성된 Scheduler를 따른다.

구독이 무슨 쓰레드에서 되었든 상관없다.

또하나의 특징은 subscribe(on:)는 PuPublisher와 Subscriber가 무슨 Scheduler에서 구독이 되냐를 제어하기 때문에 receive(on:)와 다르게 위치가 상관없다.

var bag = Set<AnyCancellable>()

let subject = CurrentValueSubject<Int, Never>(1)

subject
    .map {_ in print(Thread.isMainThread)}
    .subscribe(on: DispatchQueue.global())
    .sink { value in
        print(Thread.isMainThread)
    }.store(in: &bag)
false
false

참고한 블로그

  • https://www.vadimbulavin.com/understanding-schedulers-in-swift-combine-framework/
  • https://zeddios.tistory.com/972?category=842493
저작자표시 (새창열림)

'iOS&Swift' 카테고리의 다른 글

[Swift] @State, @Binding, @Published 대해 알아보기(Property Wrapper 1편)  (0) 2024.01.19
[Swift] Alamofire의 RequestInterceptor을 사용해 토큰 갱신하기  (0) 2024.01.18
Combine 뽀개기 3장: Subject  (0) 2023.07.26
Combine 뽀개기 2장: Subscriber  (0) 2023.07.26
Combine 뽀개기 1장: Publisher  (0) 2023.07.26
'iOS&Swift' 카테고리의 다른 글
  • [Swift] @State, @Binding, @Published 대해 알아보기(Property Wrapper 1편)
  • [Swift] Alamofire의 RequestInterceptor을 사용해 토큰 갱신하기
  • Combine 뽀개기 3장: Subject
  • Combine 뽀개기 2장: Subscriber
Esiwon
Esiwon
iOS 개발 블로그
  • Esiwon
    시원한 코드 기록
    Esiwon
  • 전체
    오늘
    어제
    • 분류 전체보기 (70)
      • iOS&Swift (24)
      • git & github (1)
      • 코테 (41)
      • 네부캠 회고 (4)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • 깃허브
  • 공지사항

  • 인기 글

  • 태그

    BFS
    네부캠
    백준
    이분탐색
    탐색
    실버
    Swift
    회고
    Race Condition
    코딩테스트
    dfs
    Property wrapper
    비동기
    ios
    photoUI
    PhotoKit
    그리디
    GCD
    브루트포스 알고리즘
    Combine
    재귀
    완전탐색
    코테
    노드
    photos
    구현
    챌린지
    다이나믹 프로그래밍
    알고리즘
    동시성
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
Esiwon
Combine 뽀개기 4장: Scheduler
상단으로

티스토리툴바