요즘 Rxswift
에 공부하고 있는데, 개념도 어렵고 Operator
는 왜이리 많은지 쉽지않다.
그래서 공부할 겸 Rxswift
를 하기전에 중요한 개념에 대해 소개해보려고 한다.
3개의 문제가 있다.
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
도 탈출 클로저이고 여기에서 input
은 T
이고
output
는 Void
타입이다.
아하 그럼 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
로 최종적으로 들어가게된다.
그리고 s
가 callback
이므로 이것에 있는 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탄에서 소개해볼 예정이다!
출처
-곰튀김님 강의 -