위 Marble diagram에는 세개의 구성요소가 있는데,
Observable은 next
이벤트를 통해 이 각각의 요소들을 방출하는 것이다.
위 Observable은 세개의 tap 이벤트를 방출한 뒤 종료된 completed
이벤트이다.
위의 경우에는 상단의 2개 예시와는 다르게 에러가 발생했다.
완전종료되긴 했지만, error
이벤트를 통해 종료되었다.
정리하자면,
1. Observable은 어떤 구성요소를 가지는
next
이벤트를 계속해서 방출할 수 있다.
2. Observable은completed
이벤트를 통해서 완전종료 될 수 있다.
3. Observable은error
이벤트를 통해서 완전종료 될 수 있다.
let observable:Observable<Int> = Observable<Int>.just(one)
just
는 Observable의 타입 메소드로, 오직 하나의 요소를 포함하는 Observable sequence를 생성한다. let observable2 = Observable.of(one, two, three)
of
는 주어진 값들의 타입 추론을 통해 Observable sequence를 생성한다. let observable4 = Observable.from([one, two, three])
from
은 일반적은 array의 각 요소들을 하나씩 방출한다.일반적으로 구독이라고 말한다.
Observable을 구독하고 싶을 때 subscribe를 선언한다.
Observable은 구독되기 전에는 아무런 이벤트도 발생하지 않는다.
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
를 갖고 Disposable
을 리턴한다.
위 코드에서 Observable은 각 요소들에 대해서 next
이벤트를 방출하고 completed
를 방출한다.
여기서 next(1)이 아닌, 1이라는 요소만을 방출하고 싶다면 어떻게 해야할까?
observable.subscribe { event in
if let element = event.element {
print(element)
}
}
위 코드와 같은 방법도 있겠지만, 다음의 연산자를 사용하면 보다 편리하다.
observable.subscribe(onNext: { element in
print(element)
})
onNext 클로저는 next 이벤트의 요소만을 처리하고 다른 것은 무시한다.
요소를 갖지 않는 Observable은 empty
연산자를 통해 completed
이벤트만 방출하게 된다.
example(of: "empty") {
let observable = Observable<Void>.empty()
}
observable.subscribe(
// 1
onNext: { element in
print(element)
},
// 2
onCompleted: {
print("Completed")
}
)
.empty
와 반대로 절대 종료되지 않는 Observable
무한 지속 시간을 나타내는 데 사용할 수 있다.
example(of: "never") {
let observable = Observable<Void>.never()
observable.subscribe(
onNext: { element in
print(element)
},
onCompleted: {
print("Completed")
}
)
}
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을 생성한다.subscription.dispose()
요소가 한정되어 있다면 dispose()
를 호출하지 않아도 completed
가 호출되고 종료되지만, 요소가 무한하다면 dispose()
를 호출해야 종료된다.
DisposeBag
타입을 이용할 수 있다.DisposeBag
은 disposables를 가지고 있다. example(of: "DisposeBag") {
// 1
let disposeBag = DisposeBag()
// 2
Observable.of("A", "B", "C")
.subscribe{ // 3
print($0)
}
.disposed(by: disposeBag) // 4
}
만약 dispose bag 코드를 추가하거나 수동으로 dispose를 호출하는 것을 하지 않는다면 메모리 누수가 일어날 것이다. 하지만 컴파일러가 경고를 주긴 함...
.next
를 통해 Observable을 만들었던 것처럼 .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()
})
.subscribe(
onNext: { print($0) },
onError: { print($0) },
onCompleted: { print("Completed") },
onDisposed: { print("Disposed") }
).disposed(by: disposeBag)
}
/* Prints:
1
Completed
Disposed
*/
create
는 escaping클로저로, AnyObserver
를 받고 Disposable
을 리턴한다.AnyObserver
는 generic타입이다.completed
되었기 때문에 3번 주석의 요소는 방출되지 않는다.completed
되기 전에 error
이벤트가 방출되었다면 에러를 통해 종료될 것이다.complete
, error
모두 방출하지 않고 disposeBag에 어떤 구독도 추가되지 않았다면 종료를 위한 이벤트가 방출되지 않고 따라서 .disposed(by: disposeBag)
이 호출되지 못해서 메모리 누수가 발생될 것이다.