RxSwift 기초 2편 - dispose/subject

구독 취소 disposing

우리가 무언가를 subscribe하기 시작하면 해당 subscribe는 value의 변동을 '항상' listening 상태로 둔다.

그 수가 많아지면 아무래도 메모리 측면에서 비효율을 야기할 것이다.

그러니 구독을 취소하는 방법도 배워보자.

 

기본형

예를 들어 다음과 같은 subscription1이 있다고 할 때, subscriber를 dispose하는 방법은 단순히 .dispose()를 원하는 시점에 호출하는 것 뿐이다.

// subscription1을 Subscriber라고 한다.

let subscription1 = observable4.subscribe(onNext: { (event) in
    print(event)
})

// dispose를 통해 memory leak을 방지.
subscription1.dispose()

이렇게 함으로써 개발자는 메모리 관리를 의도한 대로 할 수 있다.

 

disposeBag

그런데 일일이 subscriber들을 dispose하는 것은 개발자 입장에서 충분히 까먹고 지나칠 수 있는 일이다.

이 때 우리는 disposeBag이라는 것을 사용할 수 있는데,

disposeBag은 그것을 위해 subscriber들의 Observing을 "적절한 때에" dispose하는 데에 쓰인다.

 

사용형식은 다음과 같다.

disposeBag을 생성하고, subscriber가 disposed(By: disposeBag) 을 호출하면 된다.

// bag 생성
let disposeBag = DisposeBag()

// 이 disposeBag은 .disposed(by: ) 메소드와 함께 쓸 수 있다.
// 아래는 subscribe하는 것과 동시에 disposeBag에 해당 subscription의 dispose를 넣는 예제이다.
Observable.of("A", "B", "C")
    .subscribe {
        print($0)
    }.disposed(by: disposeBag)

 

예제를 하나만 더 살펴보자.

Observable을 생성함과 동시에 subscribe도 하고,

observable, subscribe이 생성되는 시점에 실행될 함수도 넣으면서

dispose까지 한번에 해보자.

아래는 그 예시이다.

 

Observable<String>.create { (observer) in
    observer.onNext("A")
    observer.onCompleted()
    observer.onNext("?")
    // create 메소드는 아래처럼 disposable을 리턴해줘야 한다.
    return Disposables.create()
}.subscribe(onNext: {print ($0)},
            onError: {print($0)},
            onCompleted: {print("OnCompleted")},
            onDisposed: {print("disposed")})
.disposed(by: disposeBag)

/*
completed
A
OnCompleted
disposed
 // ?는 출력되지 않았다. onCompleted 이후 코드는 실행되지 않는다.
*/

 

Subject

RxSwift에서 배워야 할 개념 중 하나로 Subject라는 것도 있다.

중요하고 흥미로운 개념이니 잘 이해하고 넘어가자.

 

특징

Subject는 Obeserver와 똑같이 observable하다는 특징을 갖는다.

Subject는 Event를 받아서 Subscriber에게 그것을 전달하는 역할을 한다.

 

PublishSubject

PublishSubject는 subscribe할 수 있고, event를 전달해주는 대표적인 subject이다.

 

사용 형식

let subject = PublishSubject<String>()

subject.onNext("Issue #1") // 첫 번째 이슈가 발행됐다고 가정.
subject.subscribe { (event) in
    print(event)
}
// 이 시점엔 아무것도 출력되지 않음.
subject.onNext("Issue #2") // 두 번째 이슈가 발행됐다고 가정.
// next(Issue #2)가 출력됨.
subject.onNext("Issue #3")
// next(Issue #3) dispose()

subject.dispose()
subject.onCompleted()
//아무것도 출력되지 않음.

 

publishSubject는 가장 기본적인 형태의 subject이다.

다른 목적에 맞게 subject는 몇 가지 유형이 더 있는데, 한번 살펴보자.

 

BehaviorSubject

BehaviorSubject는 초기값을 지정할 수 있다.

예를 들어 아래처럼 "Issue #1" 같은 초기값을 지정할 수 있다는 소리다.

초기값은 subscribe시 읽을 수 있다.

 

예제

// initialValue를 요구.
let behaviorSubject = BehaviorSubject(value: "Issue #1")
behaviorSubject.subscribe { event in
    print(event)
}
behaviorSubject.onNext("Issue #2")
// 1, 2가 모두 출력됨.
// next(Issue #1)
// next(Issue #2)

 

RelaySubject

RelaySubject는 따로 버퍼 사이즈를 지정해줘야 한다.

이 버퍼사이즈는 subscribe 하는 시점에 사용되는데,

해당 시점에 버퍼 사이즈 만큼 최신에 발행된 event를 가져온다.

stack과 같이 LIFO이다.

 

// 버퍼 사이즈를 지정해줘야 하는 subject.
// `subscribe 시점에` 버퍼 사이즈 만큼 최신에 발행된 onNext event를 가져옴.
let replaySubject = ReplaySubject<String>.create(bufferSize: 2)
replaySubject.onNext("Issue #1")
replaySubject.onNext("Issue #2")
replaySubject.onNext("Issue #3")

replaySubject.subscribe { event in
    print(event)
}
// 출력:
// next(Issue #2)
// next(Issue #3)

replaySubject.onNext("Issue #4")
replaySubject.onNext("Issue #5")
replaySubject.onNext("Issue #6")

print("[ReplaySubscription 2]")
replaySubject.subscribe { event in
    print(event)
}
// 출력:
// [ReplaySubscription 2]
// next(Issue #5)
// next(Issue #6)

 

BehaviorRelay

BehaviorRelay는 어떤 변수를 생성하고, 그것을 Observable하게 만들 때 쓰인다.

원래는 Variable이라는 자료형을 제공했으나, Variable은 곧 deprecate될 예정이다.

초기값을 지정해야 하고, RxCocoa를 요구한다.

따라서 podfile에 pod 'RxSwift', '~> 4.0'을 적어주고 pod install로 설치한 후 import까지 해준다.

 

예제

BehaviorRelay는 만듦과 동시에 subscribe할 수 있으며, .accept로 값을 대치할 수 있다.

import RxSwift
import RxCocoa

// relay는 어떤 변수를 생성하고 그것을 Observable하게 만들 때 쓰인다.
// 초기값을 지정해주어야 한다.
// BehaviorRelay는 RxCocoa가 pod install 되어 있어야 한다.
let relay = BehaviorRelay(value: "This is Initial Value")

// 생성한 변수`.asObservable()` 을 이용해 observable로 만들 수 있다.
// 만듦과 동시에 .subscribe로 구독할 수 있다.
relay.asObservable()
    .subscribe {
        print ($0)
    }

// 변수의 값을 accept로 변경하는 법:
relay.accept("Hello World")

/* 여기까지 실행 시 출력문:
 next(This is Initial Value)
 next(Hello World)
 */

 

예제2 - value 추가하기.

.accept는 값을 대치 하지만 값의 추가가 필요할 때도 있을 것이다.

그것은 value를 받아오고 value에 값을 추가한 뒤 .accept(value) 함으로써 가능하다.

코드로 살펴보자.

 

// array자료형을 추가할 수도 있다.
// 아래는 emptyArray를 추가하는 예제.
//let relayArray = BehaviorRelay(value: [String]())

// 초기화하면서 변수를 생성.
let relayArray = BehaviorRelay(value: ["Item 0"])


relayArray.asObservable()
    .subscribe {
        print ($0)
    }


/*출력
 next(["Item0"])
 */


// Array의 값을 '대치'하는 것은 다음과 같이 가능하다.
relayArray.accept(["Item 1"])

/*출력
 next(["Item1"])
 */

// 값을 이어 붙이고 싶을 때:

var value = relayArray.value
value.append("item 2")
value.append("item 3")

relayArray.accept(value)


/*출력
 next(["Item1", "item2", "item3"])
 */
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기
// custom