[Swift] RxSwift - Observable

호랭이·2022년 5월 11일
0

🍎 RxSwift

목록 보기
1/6
post-custom-banner

Observable이란?

  • Rx의 심장
  • observable = observable sequence = sequence
  • 이 모든 것은 비동기적
  • Observable은 일정기간동안 계속해서 이벤트를 만들고, 이것을 emitting이라고 한다.
  • 각 이벤트는 값을 가질 수 있으며, 제스처를 인식할 수도 있다.

Observable Life Cycle


위 Marble diagram에는 세개의 구성요소가 있는데,
Observable은 next 이벤트를 통해 이 각각의 요소들을 방출하는 것이다.


위 Observable은 세개의 tap 이벤트를 방출한 뒤 종료된 completed 이벤트이다.


위의 경우에는 상단의 2개 예시와는 다르게 에러가 발생했다.
완전종료되긴 했지만, error 이벤트를 통해 종료되었다.

정리하자면,

1. Observable은 어떤 구성요소를 가지는next 이벤트를 계속해서 방출할 수 있다.
2. Observable은 completed 이벤트를 통해서 완전종료 될 수 있다.
3. Observable은 error 이벤트를 통해서 완전종료 될 수 있다.

Observable 만들기

.just

let observable:Observable<Int> = Observable<Int>.just(one)
  • just는 Observable의 타입 메소드로, 오직 하나의 요소를 포함하는 Observable sequence를 생성한다.

.of

 let observable2 = Observable.of(one, two, three)
  • of는 주어진 값들의 타입 추론을 통해 Observable sequence를 생성한다.

.from

 let observable4 = Observable.from([one, two, three])
  • from은 일반적은 array의 각 요소들을 하나씩 방출한다.

Subscribe

.subscribe

일반적으로 구독이라고 말한다.
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)
 }
}

위 코드와 같은 방법도 있겠지만, 다음의 연산자를 사용하면 보다 편리하다.

.subscribe(onNext:)

observable.subscribe(onNext: { element in
  print(element)
})

onNext 클로저는 next 이벤트의 요소만을 처리하고 다른 것은 무시한다.

.empty()

요소를 갖지 않는 Observable은 empty 연산자를 통해 completed 이벤트만 방출하게 된다.

example(of: "empty") {
  let observable = Observable<Void>.empty()
}

observable.subscribe(
  // 1
  onNext: { element in
    print(element)
  },

  // 2
  onCompleted: {
    print("Completed")
  }
)
  • 요소를 갖지 않기 때문에 타입추론이 불가능하다. 따라서 타입을 명시해줘야한다.
  • 즉시 종료할 수 있는 Observable을 리턴하고 싶을 때 사용
  • 의도적으로 0개의 값을 가지는 Observable을 리턴하고 싶을 때 사용

.never()

.empty와 반대로 절대 종료되지 않는 Observable
무한 지속 시간을 나타내는 데 사용할 수 있다.

example(of: "never") {
  let observable = Observable<Void>.never()

  observable.subscribe(
    onNext: { element in
      print(element)
    },
    onCompleted: {
      print("Completed")
    }
  )
}

.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)
  })
}
  1. range 연산자를 이용해서 start부터 count만큼의 값을 갖는 Observable을 생성한다.
  2. 방출된 요소들에 대한 n번째 피보나치 숫자를 계산하고 출력한다.

Disposing 및 종료

  • Observable은 subscription을 받기 전까진 아무것도 하지 않는다.
  • subscribe가 이벤트 방출의 트리거가 되는 셈인데, 반대로 이 subscribe를 취소함으로써 Observable을 수동 종료시킬 수 있다.

.dispose()

subscription.dispose()

요소가 한정되어 있다면 dispose()를 호출하지 않아도 completed가 호출되고 종료되지만, 요소가 무한하다면 dispose()를 호출해야 종료된다.

DisposeBag()

  • 수동적으로 각각 관리하는 것은 비요율적이기 때문에, RxSwift에서 제공하는 DisposeBag 타입을 이용할 수 있다.
  • DisposeBag은 disposables를 가지고 있다.
  • 이 disposables는 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. disposeBag 생성
  2. observable 생성
  3. 이벤트 출력
  4. 방출된 값을 disposeBag에 추가

만약 dispose bag 코드를 추가하거나 수동으로 dispose를 호출하는 것을 하지 않는다면 메모리 누수가 일어날 것이다. 하지만 컴파일러가 경고를 주긴 함...

.create(:)

.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타입이다.
  • 2번 주석에서 completed되었기 때문에 3번 주석의 요소는 방출되지 않는다.
  • 만약 completed되기 전에 error 이벤트가 방출되었다면 에러를 통해 종료될 것이다.
  • 만약 complete, error 모두 방출하지 않고 disposeBag에 어떤 구독도 추가되지 않았다면 종료를 위한 이벤트가 방출되지 않고 따라서 .disposed(by: disposeBag)이 호출되지 못해서 메모리 누수가 발생될 것이다.
profile
삐약
post-custom-banner

0개의 댓글