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
- map
- Xcode
- swift documentation
- clean architecture
- Clean Code
- SWIFT
- uiscrollview
- 클린 코드
- Observable
- MVVM
- combine
- collectionview
- UICollectionView
- Protocol
- ribs
- 리펙토링
- 리팩토링
- tableView
- 애니메이션
- swiftUI
- 스위프트
- ios
- HIG
- uitableview
- RxCocoa
- Refactoring
- 리펙터링
- rxswift
- Human interface guide
- UITextView
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - swift] RxSwift의 MainScheduler.instance와 MainScheduler.asyncInstance 차이 본문
iOS 응용 (swift)
[iOS - swift] RxSwift의 MainScheduler.instance와 MainScheduler.asyncInstance 차이
jake-kim 2023. 1. 23. 22:32MainScheduler란?
// https://github.com/ReactiveX/RxSwift/blob/25d35f564b95ff4610b78f622c77ec3317aff31c/RxSwift/Schedulers/MainScheduler.swift#L24
Abstracts work that needs to be performed on `DispatchQueue.main`. In case `schedule` methods are called from `DispatchQueue.main`, it will perform action immediately without scheduling.
This scheduler is usually used to perform UI work.
Main scheduler is a specialization of `SerialDispatchQueueScheduler`.
This scheduler is optimized for `observeOn` operator. To ensure observable sequence is subscribed on main thread using `subscribeOn`
operator please use `ConcurrentMainScheduler` because it is more optimized for that purpose.
- MainScheduler는 DispatchQueue.main를 wrapping한 구조이며 RxSwift에서 observe(on:)에서 사용
- 만약 subscribe(on:)을 사용하고 싶은 경우는 MainScheduler가 아닌, ConurrentMainScheduler를 사용하는것이 최적화되어 있음을 주의
- observe(on:)과 subscribe(on:)의 차이는 이전 포스팅 글 참고
MainScheduler.instance와 MainScheduler.asyncInstance 차이
- instance, asyncInstance 모두 싱글톤이며, MainScheduler 클래스 안에 존재
public final class MainScheduler : SerialDispatchQueueScheduler {
...
/// Singleton instance of `MainScheduler`
public static let instance = MainScheduler()
/// Singleton instance of `MainScheduler` that always schedules work asynchronously
/// and doesn't perform optimizations for calls scheduled from main queue.
public static let asyncInstance = SerialDispatchQueueScheduler(serialQueue: DispatchQueue.main)
...
}
- 위 코드를 보면 asyncInstance도 DispatchQueue.main을 사용하고, instance도 자신의 클래스 MainScheduler() 인스턴스를 생성하여 사용하는데, 이 안에서 mainQueue는 DispatchQueue.main을 사용
public init() {
self.mainQueue = DispatchQueue.main
super.init(serialQueue: self.mainQueue)
}
- 그리고 MainScheduler 클래스는 SerialDispatchQueueScheduler를 상속받고 있는 상태이며, asyncInstance는 SerialDispatchQueueScheduler에 DispatchQueue.main인스턴스를 주입하여 생성
- 둘 다 DispatchQueue.main 인스턴스를 사용하지만, 차이점은 instance는 MainScheduler 클래스의 인스턴스를 사용한다는 것
- MainScheduler 클래스에서 SerialDispatchQueueScheduler의 메소드를 오버라이딩 하고 있는 것을 보면 차이점 확인이 가능
MainScheduler와 SerialDispatchQueueScheduler 동작 차이점
- 둘 다 모두 생성자에서 DispatchQueue.main 인스턴스를 주입받아서 action에 대해서 scheduler() 하는 메소드가 있고 여기서 차이점 확인이 가능
- schedule 동작은 작업의 갯수를 가지고 작업의 순서나 작업의 양을 제어하는 작업
- 코드를 보기 전에 앞서, 결론부터 instance와 asyncInstance 차이는 intance는 바로 현재가 MainScheduler이면 queue.async {}로 실행하지 않고 바로 실행 시켜서 (sync)하게 동작되도록 하고 asyncInstance는 바로 async로 동작
1) MainScheduler.instance 동작
- 현재가 main 스레드이고 이전 작업이 하나도 없으면 바로 sync하게 동작시키는 것 (if DispatchQueue.isMain &&... 부분의 코드)
// MainScheduler.swift
override func scheduleInternal<StateType>(_ state: StateType, action: @escaping (StateType) -> Disposable) -> Disposable {
let previousNumberEnqueued = increment(self.numberEnqueued)
if DispatchQueue.isMain && previousNumberEnqueued == 0 {
let disposable = action(state)
decrement(self.numberEnqueued)
return disposable
}
let cancel = SingleAssignmentDisposable()
self.mainQueue.async {
if !cancel.isDisposed {
cancel.setDisposable(action(state))
}
decrement(self.numberEnqueued)
}
return cancel
}
2) MainScheduler.asyncInstance 동작
- 바로 async하게 동작
func schedule<StateType>(_ state: StateType, action: @escaping (StateType) -> Disposable) -> Disposable {
let cancel = SingleAssignmentDisposable()
self.queue.async {
if cancel.isDisposed {
return
}
cancel.setDisposable(action(state))
}
return cancel
}
결론
- MainScheduler.instance는 바로 sync하게 동작하며, MainScheduler.asyncInstance는 바로 async하게 동작
- 이미 메인스레드인 경우, instance는 동기적으로 이벤트를 바로 처리할 수 있고, asyncInstance는 비동기 이벤트 처리를 보장
- 웬만하면 instance를 사용하되, 특이 케이스는 asyncInstance를 사용할 것
- 특이 케이스: 해당 스트림에서 재귀적인 접근이 동작할때, Rx 내부적으로 이런 동작은 async에서 처리하게끔 되어있어서 아래처럼 경고 메시지가 뜨는데 이 경우에 asyncIntance를 사용할 것
// https://github.com/RxSwiftCommunity/RxBiBinding/issues/20
⚠️ Reentrancy anomaly was detected.
Debugging: To debug this issue you can set a breakpoint in /Users/antonioscardigno/Documents/Sogetel/repository-forward/SOGETEL-RUMORS/DEV/iOSProjects/branches/newUI/Pods/RxSwift/RxSwift/Rx.swift:97 and observe the call stack.
Problem: This behavior is breaking the observable sequence grammar. next (error | completed)?
This behavior breaks the grammar because there is overlapping between sequence events.
Observable sequence is trying to send an event before sending of previous event has finished.
Interpretation: This could mean that there is some kind of unexpected cyclic dependency in your code,
or that the system is not behaving in the expected way.
Remedy: If this is the expected behavior this message can be suppressed by adding .observeOn(MainScheduler.asyncInstance)
or by enqueing sequence events in some other way.
* 참고
'iOS 응용 (swift)' 카테고리의 다른 글
Comments