Rxswift 기본 1탄

Eddy📱·2022년 7월 8일
0

RxSwift

목록 보기
3/4
post-thumbnail

요즘 Rxswift에 공부하고 있는데, 개념도 어렵고 Operator는 왜이리 많은지 쉽지않다.

그래서 공부할 겸 Rxswift를 하기전에 중요한 개념에 대해 소개해보려고 한다.

3개의 문제가 있다.
sync/async, blocking/non-blocking 인지를 코드를 보고 분석해보는 것이다.
각각의 개념은 아래와 같다

  • Sync: 한 작업이 끝나고 결과를 받은 후에 다음 작업을 수행한다.
  • Async: 한 작업이 시작되고 결과가 오기 전에 다음 작업을 수행한다.
  • Blocking: 한 작업이 수행되는 동안에 다른 작업은 수행되지 못한다.
  • Non-Blocking: 서로 다른 작업이 함께 수행된다.

이제 문제를 풀어보자!

func i2s(_ i: Int) -> String {
    "\\(i)"
}

let s = i2s(42)
print(s)

정답은 Sync, blocking이다.
i를 전달받고 내부에서 이를 호출하는 순서로 다음 작업을 수행한다.
또한 한 작업이 수행하는 동안 다음 작업을 수행하지 못한다.

func i2s(_ i: Int, _ cb: (String) -> Void) {
    cb("\\(i)")
}

i2s(42) { s in
    print(s)
}

정답은 Sync, blocking이다.

이것도 위와 동일하다. 전달 받고, 이를 호출하는 순서로 다음 작업이 수행된다.
i를 입력받고 이를 cb의 클로저에서 돌며 출력하게 된다.

다음 문제는 아래와 같다.

func i2s(_ i: Int, _ cb: @escaping (String) -> Void) {
    DispatchQueue.global(qos: .background).async {
        cb("\\(i)")
    }
}

i2s(42) { s in
    print(s)
}

정답은 바로 async/blocking이다

호출될 때 시간 순서를 보면 먼저 i2s(42)가 시작되고 바로 끝이 난다.

그 다음에는 내부 탈출 클로저가 실행되는 순서다.

왜냐하면 DispatchQueue.global.async로 비동기적으로 global Thread에서 동작한다.

그래서 Thread에 callback인 cb("\\(i)") 를 넣어두고, 이후 실행하는 방식이다.

그러므로 이 부분이 마지막에 실행되는 것이다.

순서대로 실행될 것같은 코드도 이렇게 다르게 호출하는 방식을 구현할 수 있다.

이를 async하게 <T>를 전달하는 방식으로 코드를 만든다면 아래처럼 될 것이다.

func generalAsyncFunction<T>(task: @escaping (_ result: @escaping (T) -> Void) -> Void,
                             execute: @escaping (T) -> Void) {
    DispatchQueue.global(qos: .background).async {
        task(execute)
    }
}

generalAsyncFunction(task: { cb in cb("\\(42)") },
                     execute: { s in print(s) })

이를 정확하게 이해하는 것이 중요하다!!!! Rxswift에서 메인 지식일듯싶다!

천천히 하나씩 파헤쳐 보도록 한다.

파라미터를 보면 매우 복잡해 보이는데, 실제 하나씩 보면 이해하기 쉬울 것이다.

먼저 task 파라미터에 탈출 클로저로

(_ result: @escaping (T) -> Void) 

가 들어가 있다.

이는 파라미터 인자로 함수가 들어갔다고 표현한다.

그래서 이곳을 먼저 확인해주는 것이 중요하다.

result도 탈출 클로저이고 여기에서 inputT이고
outputVoid 타입이다.

아하 그럼 T를 받아서 Void 타입을 반환하겠구나 생각하면 된다.

그럼 아무것도 내보내지 않는다라고 생각할 수 있다.

하지만 다음 파라미터인 execute를 보면 동일한 구조로 되어있는 것을 볼 수 있다.

아 이곳에서 값을 받는구나! 그럼 그 값을 어디서 활용해?

바로 task에서 마지막에 이 값을 가지고 task를 돌리는 것이다!!!

그럼 다음 내부로 들어가면 global thread에서 비동기로 task(execute) 를 실행하는 것을 볼 수 있다.

execute가 안에 있는 이유를 이제 알겠죠? task가 호출한 함수인 result의 결과값을 execute가 가지게 되고 이를

마지막에 task에서 호출하기 때문이다

generalAsyncFunction(task: { cb in cb("\\(42)") },
                     execute: { s in print(s) })

cb("\\(42)" 가 이제 result가 돌아가는 것이고 그 값이 s로 가게 된다음 그게 cb로 최종적으로 들어가게된다.

그리고 scallback이므로 이것에 있는 print 출력을 하게 되며 마무리가 된다!

복잡해보이지만 하나씩 코드를 파헤쳐본다면 이해할 수 있다.

이제 Rxswift의 핵심 기능이다.

이걸 Rxswift 느낌으로 바꾸어 본다면 아래와 같다

class MyObservable<T> {
    private var _task: ((@escaping (T) -> Void) -> Void)?

    init(task: @escaping (_ execute: @escaping (T) -> Void) -> Void) {
        self._task = task
    }

    func subscribe(_ execute: @escaping (T) -> Void) {
        guard let task = _task else { return}
		    DispatchQueue.global(qos: .background).async {
            task(execute)
        }
    }
}

MyObservable(task: { cb in cb("\\(42)") })
		.subscribe { s in print(s) }

task는 위의 task 일 것이고, execute도 동일하다.
생성 시에 task를 받게 구현되어 있다.

이 부분이 Rxswift를 이해하기 전에 가장 먼저 알아야될 개념인듯싶다

이제 진짜 Rxswift의 기능들 몇가지를 2탄에서 소개해볼 예정이다!

출처
-곰튀김님 강의 -

profile
Make a better world

0개의 댓글