카테고리 없음

RxSwift) relay, dispose, binding

나태한개발 2024. 10. 15. 05:46

Relay란?

subject 와 비슷한 동작을 수행하지만 Subject, Observable과 달리 .completed, .error 이벤트가 없다. 심지어 .next 이벤트가 아닌 accept(_:) 메소드를 사용합니다.

 

Relay 종류

PublishRelay

publish relay는 publish subject의 동작과 유사하다. 구독자는 구독하고 난 이후에 방출되는 이벤트를 받아 처리하게 된다.

class ViewController: UIViewController {

    let disposeBag = DisposeBag()
    let button = UIButton(type: .system)
    let publishRelay = PublishRelay<Void>() // 버튼 클릭 이벤트를 처리할 PublishRelay

    override func viewDidLoad() {
        super.viewDidLoad()

        view.addSubview(button)

        // 버튼 클릭 이벤트를 PublishRelay에 바인딩
        button.rx.tap
            .bind(to: publishRelay) // 이벤트가 발생하면 publishRelay로 전달해줌
            .disposed(by: disposeBag)

        // PublishRelay의 구독: 버튼 클릭 시 동작 처리
        publishRelay.subscribe(onNext: {
            print("Button was tapped!")
        }).disposed(by: disposeBag)
    }
}

 

BehaviorRelay

Behavior Relay는 BehaviorSubject 처럼 동작하지만 다른 점이 있다. 우선 BehaviorRelay는 현재 값(element)을 출력할 수 있다. 구독을 시작 하면 가장 최근값을 먼저 방출 한다는 뜻이다.

class ViewController: UIViewController {

    let disposeBag = DisposeBag()
    let textField = UITextField()
    let resultLabel = UILabel()
    let behaviorRelay = BehaviorRelay<String>(value: "") // 입력값을 저장할 BehaviorRelay

    override func viewDidLoad() {
        super.viewDidLoad()

        view.addSubview(textField)
        view.addSubview(resultLabel)

        // BehaviorRelay의 구독: 텍스트 필드의 값을 레이블에 표시
        behaviorRelay.asObservable()
            .subscribe(onNext: { [weak self] text in
                self?.resultLabel.text = text
            })
            .disposed(by: disposeBag)

        // 텍스트 필드의 값 변경 시 BehaviorRelay에 업데이트
        textField.rx.text.orEmpty
            .bind(to: behaviorRelay)
            .disposed(by: disposeBag)
    }
}

 

ReplayRelay

ReplayRelay는 지정된 버퍼 크기만큼 이전 이벤트를 저장하고, 새로운 구독자가 구독을 시작할 때 그 버퍼에 저장된 이벤트들을 방출한다.

class ViewController: UIViewController {

    let disposeBag = DisposeBag()
    let textField = UITextField()
    let resultLabel = UILabel()
    let replayRelay = ReplayRelay<String>.create(bufferSize: 3) // 최대 3개의 이전 값을 저장할 ReplayRelay

    override func viewDidLoad() {
        super.viewDidLoad()

        view.addSubview(textField)
        view.addSubview(resultLabel)

        // ReplayRelay의 구독: 입력된 텍스트를 레이블에 표시
        replayRelay.asObservable()
            .subscribe(onNext: { [weak self] text in
                self?.updateLabel(with: text) // 레이블 업데이트
            })
            .disposed(by: disposeBag)

        // 텍스트 필드의 값 변경 시 ReplayRelay에 업데이트
        textField.rx.text.orEmpty
            .subscribe(onNext: { [weak self] text in
                guard let self = self, !text.isEmpty else { return }
                self.replayRelay.accept(text) // 입력된 텍스트를 ReplayRelay에 저장
            })
            .disposed(by: disposeBag)
    }

    // 레이블에 이전 입력값을 추가하는 메서드
    private func updateLabel(with text: String) {
        var currentText = resultLabel.text ?? ""
        currentText += "\(text)\n" // 새로운 입력값 추가
        resultLabel.text = currentText
    }
}

 

Dispose

Dispose 사용이유

일단 기본적으로 dispose는 Observable에 대한 구독을 해제 할 때 사용한다. 그리고 더 중요한 목적은 메모리 관리를 위해서 이다. Observable은 기본적으로, complete이나 error가 발생하기 전까진 계속 이벤트를 방출 시킨다. 그래서 메모리에서 해제 시켜주지 않으면 메모리 릭이 발생할 가능성이 크다. 그렇기 때문에 dispose를 호출하여 메모리에서 해제 시킨다. complete이나 error가 호출 되도 dispose가 호출 된다.

 

Relay에서 Dispose

relay에서는 complete이나 error가 없기 때문에 메모리에서 해제시키기 위해서 dispose를 호출해 주어야 한다.

 

Binding

RxSwift에서의 Binding

RxSwift에서의 binding은 Observable의 값을 다른 Observer에 연결하는 것을 의미하는데 relay를 사용한 예시를 보겠다.

let button = UIButton(type: .system)
let publishRelay = PublishRelay<Void>()

button.rx.tap
    .bind(to: publishRelay) // bind 함수를 사용한 바인딩
    .disposed(by: disposeBag)

publishRelay.subscribe(onNext: {
    print("Button was tapped!")
}).disposed(by: disposeBag)

bind 함수를 사용하여 button.rx.tap에서 방출한 이벤트를 publishRelay에 전달한다.