지난 포스팅에 이어 이번엔 Observable을 알아보려 합니다 . !
차근 차근 살펴보며 이해해봅시다 . . !
Observable이란 ?
Observable들은 일정 기간동안 계속해서 이벤트를 생성하며 이러한 과정을 emitting(방출)이라고 표현합니다.
각각의 이벤트들은 숫자나 커스텀한 인스턴스 등과 같은 값을 가질 수 있고, 탭과 같은 제스처를 인식할 수도 있습니다.
Observable의 생명주기
Observable은 next 이벤트를 통해 각각의 요소들을 방출합니다.
이 Observable은 세 개의 tap 이벤트를 방출한 뒤 완전 종료됩니다. 이를 completed 이벤트라고 합니다.
이 예시는 에러가 발생한 상황으로 Observable이 완전 종료되었다는 면에서는 다를 게 없지만 error 이벤트를 통해 종료된 것입니다.
핵심은 아래와 같습니다.
- Observable은 어떤 구성요소를 가지는 next 이벤트를 계속해서 방출할 수 있다.
- Observable은 error 이벤트를 방출하여 완전 종료될 수 있다.
- Observable은 complete 이벤트를 방출하여 완전 종료될 수 있다.
/// Represents a sequence event.
///
/// Sequence grammar:
/// **next\* (error | completed)**
public enum Event<Element> {
/// Next elemet is produced.
case next(Element)
/// Sequence terminated with an error.
case error(Swift.Error)
/// Sequence completed successfully.
case completed
}
- .next 이벤트는 어떠한 Element 인스턴스를 가진다.
- .error 이벤트는 Swift.Error 인스턴스를 가진다.
- completed 이벤트는 아무런 인스턴스를 가지지 않고 단순히 이벤트를 종료시킨다.
Observable 만들기
example(of: "just, of, from") {
// 1
let one = 1
let two = 2
let three = 3
//2
let observable:Observable<Int> = Observable<Int>.just(one)
}
just는 Observable의 타입 메소드로 오직 하나의 요소를 포함하는 Observable sequence를 생성합니다.
그러므로 위 코드의 observable은 1을 방출할 것입니다.
let observable2 = Observable.of(one, two, three)
observable2의 타입은 Observable<Int> 입니다.
.of 연산자는 주어진 값들의 타입추론을 통해 Observable sequence를 생성합니다.
따라서 어떠한 array를 observable array로 만들고 싶다면 array를 .of 연산자에 넣으면 됩니다.
let observable3 = Observable.of([one, two, three])
observable3의 타입은 Observable<[Int]> 입니다.
이렇게 하면 just 연산자를 쓴 것과 같이 [1, 2, 3] 를 단일요소로 가지게 됩니다.
let observable4 = Observable.from([one, two, three])
observable4의 타입은 Observable<Int> 입니다.
from 연산자는 일반적인 array 각각 요소들을 하나씩 방출합니다.
from 연산자는 오직 array만 취합니다.
Observable 구독
Observable을 구독하고 싶을 때 구독(subscribe)!을 선언합니다.
Observable은 실제로 sequence 정의일 뿐이므로 Observable은 subscriber, 즉 구독되기 전에는 아무런 이벤트도 보내지 않습니다.
1. subscribe()
example(of: "subscribe") {
let one = 1
let two = 2
let three = 3
let observable = Observable.of(one, two, three)
observable.subscribe({ (event) in
print(event)
})
/* Prints:
next(1)
next(2)
next(3)
completed
*/
}
.subscribe는 escaping 클로저로 Int 타입을 Event로 갖습니다.
escaping에 대한 리턴값은 없으며 .subscribe는 Disposable을 리턴합니다.
Observable은 각각의 요소들에 대해 .next 이벤트를 방출했으며 최종적으로 .completed를 방출했습니다.
2. subscribe(onNext:)
observable.subscribe(onNext: { (element) in
print(element)
})
/* Prints:
1
2
3
*/
.onNext 클로저는 .next 이벤트만을 argument로 취한 뒤 핸들링할 것이고 다른 것들은 모두 무시하게 됩니다.
3. empty()
example(of: "empty") {
let observable = Observable<Void>.empty()
observable.subscribe(
// 1
onNext: { (element) in
print(element)
},
// 2
onCompleted: {
print("Completed")
}
)
}
/* Prints:
Completed
*/
요소를 하나도 가지지 않는 Observable로 empty 연산자를 통해 .complete 이벤트만 방출하게 됩니다.
언제 사용할까요 ?
- 즉시 종료할 수 있는 Observable을 리턴하고 싶을 때
- 의도적으로 0개의 값을 가지는 Observable을 리턴하고 싶을 때
4. never()
example(of: "never") {
let observable = Observable<Any>.never()
observable
.subscribe(
onNext: { (element) in
print(element)
},
onCompleted: {
print("Completed")
}
)
}
empty와 반대되는 연산자로 Completed 조차 프린트되지 않습니다.
5. range()
example(of: "range") {
//1
let observable = Observable<Int>.range(start: 1, count: 10)
observable
.subscribe(onNext: { (i) in
//2
let n = Double(i)
let fibonacci = Int(((pow(1.61803, n) - pow(0.61803, n)) / 2.23606).rounded())
print(fibonacci)
})
}
range 연산자를 이용해 start부터 count 크기 만큼의 값을 갖는 Observable를 생성합니다.
각각 방출된 요소에 대한 n번째 피보나치 숫자를 계산하고 출력합니다.
Disposing과 종료
Observable은 앞서 말했듯이 subscription을 받기 전까진 아무짓도 하지 않습니다.
즉, subscription이 Observable이 이벤트를 방출하도록 해줄 방아쇠 역할을 한다는 의미입니다.
따라서 Observable에 대한 구독을 취소함으로써 Observable을 수동적으로 종료시킬 수 있습니다.
1. dispose()
example(of: "dispose") {
// 1
let observable = Observable.of("A", "B", "C")
// 2
let subscription = observable.subscribe({ (event) in
// 3
print(event)
})
subscription.dispose()
}
1. String의 Observable 생성
2. 이 Observable 구독
3. 각각의 이벤트들 프린트
이 때, 구독을 취소하고 싶으면 dispose()를 호출하면 됩니다. 구독을 취소하거나 dispose 한 뒤에는 이벤트 방출이 정지됩니다.
현재 observable 안에는 3개의 요소만 있으므로 dispose()를 호출하지 않아도 Completed가 프린트되지만, 요소가 무한히 있다면 dispose()를 호출해야 Completed가 프린트됩니다.
2. DisposeBag()
각각의 구독에 대해 일일히 관리하는 것은 효율적이지 못하기 때문에 RxSwift에서 제공하는 DisposedBag 타입을 사용할 수 있습니다.
DisposeBag는 disposables를 가지고 있는데 disposable은 dispose bag이 할당 해제하려고 할 때마다 dispose()를 호출합니다.
example(of: "DisposeBag") {
// 1
let disposeBag = DisposeBag()
// 2
Observable.of("A", "B", "C")
.subscribe{ // 3
print($0)
}
.disposed(by: disposeBag) // 4
}
1. dispose bag 생성
2. observable 생성
3. 방출하는 이벤트 출력
4. subscribe로부터 방출된 리턴 값을 disposeBag에 추가
왜 이런짓을 할까 .. ?
만약 dispose bag을 subscription에 추가하거나 수동적으로 dispose를 호출하는 것을 빼먹는다면, 메모리 누수가 발생할 수 있기 때문입니다.
3. create(:)
example(of: "create") {
let disposeBag = DisposeBag()
Observable<String>.create({ (observer) -> Disposable in
// 1
observer.onNext("1")
// 2
observer.onCompleted()
// 3
observer.onNext("?")
// 4
return Disposables.create()
})
}
create는 escaping 클로저로 escaping에서는 AnyObserver를 취한 뒤 Disposable을 리턴합니다.
1. .next 이벤트를 Observer에 추가
2. .completed 이벤트를 Observer에 추가
3. 추가로 .next 이벤트를 추가
4. disposable 리턴
여기서 질문 !
.onNext 이벤트의 요소인 ? 는 subscribe에 방출될까요 ?
아래와 같이 subscribe를 찍어봅시다.
example(of: "create") {
let disposeBag = DisposeBag()
Observable<String>.create({ (observer) -> Disposable in
// 1
observer.onNext("1")
// 2
observer.onCompleted()
// 3
observer.onNext("?")
// 4
return Disposables.create()
})
.subscribe(
onNext: { print($0) },
onError: { print($0) },
onCompleted: { print("Completed") },
onDisposed: { print("Disposed") }
).disposed(by: disposeBag)
}
/* Prints:
1
Completed
Disposed
*/
.onComplete()를 통해 해당 Observable은 종료되었으므로 두 번째 onNext(_:) 는 방출되지 않습니다.
그렇다면 아래와 같이 .completed 이벤트를 방출하지 않고 disposeBag에 어떠한 구독도 추가하지 않는다면 어떻게 될까요 ?
example(of: "create") {
enum MyError: Error {
case anError
}
let disposeBag = DisposeBag()
Observable<String>.create { observer in
// 1
observer.onNext("1")
// 2
observer.onNext("?")
// 3
return Disposables.create()
}
.subscribe(
onNext: { print($0) },
onError: { print($0) },
onCompleted: { print("Completed") },
onDisposed: { print("Disposed") }
)
}
/* Prints:
1
?
*/
두 개의 onNext 요소인 1 과 ? 가 모두 찍힐 것입니다.
하지만 종료를 위한 이벤트도 방출하지 않고 .disposed(by: disposeBag)도 하지 않기 때문에 결과적으로 메모리 낭비가 발생하게 될 것입니다.
'iOS > RxSwift' 카테고리의 다른 글
[RxSwift] Combining Operators (0) | 2022.11.18 |
---|---|
[RxSwift] Transforming Operators (0) | 2022.11.17 |
[RxSwift] Filtering Operators (0) | 2022.10.12 |
[RxSwift] Subjects (0) | 2022.10.12 |
[RxSwift] Hello, RxSwift ! (1) | 2022.10.11 |