유튜브 곰튀김님 강의를 토대로 RxSwift의 기초를 정리해보았습니다. 개인 공부 목적으로 정리한 내용이라 부족한 부분이 있거나 오류가 있을 수도 있으니 언제든 알려주시면 정정하도록 하겠습니다.
[1교시] 개념잡기 - RxSwift를 사용한 비동기 프로그래밍
RxSwift 사용 이유 중 하나로는 비동기적으로 생기는 결과값을 리턴값으로 전달하기 위해서이다. (나중에 생기는 데이터를 리턴값으로 전달)
기존 : 비동기로 생성되는 데이터를 처리하기 위해 completion 등을 만들어 전달했었음. 그러나 completion이 아닌 return 값으로 전달을 해주고 싶었음.
class Observable/*나중에생기는데이터*/<T> { //나중에 생기는 데이터
private let task: (@escaping (T) -> Void) -> Void
//생성하면서 클로져를 받아서 갖고있다가 나중에 오면 메소드가 실행될때 또 클로져를 받아서 아까 저장해뒀던 그 클로져를 실행하면서 지금 들어오는 그 클로져를 전달해주면 나중에오면 메소드에 전달
init(task: @escaping (@escaping (T) -> Void) -> Void) {
self.task = task
}
func subscribe/*나중에오면*/(_ f: @escaping (T) -> Void) { //나중에 오면
task(f)
}
}
- Rxswift가 제공해주는 '나중에 생기는 데이터'라는 클래스는 Observable임.
- Observable 만드는 법 : observable을 create를 호출해서 emitter에다 next로 데이터를 전달하거나 적절한 시점에 error 또는 complete을 보내면 됨. 중간에 동작을 취소하려고 하면 disposable.create
- Observable의 데이터를 꺼내 쓰려면 subscribe 을 해야함.
- Subscribe 방법 : subscribe을 해서 받는 이벤트에 next, error, complete 이벤트를 받아서 처리.
- Subscribe 할 때에도 내가 처리할 이벤트만 따로 지정해서 사용하는 방법. subscribe 하고 나면 리턴 값으로 Disposable이 나옴. 평소에는 큰 의미없음. 특별히 취소하고 싶은 동작이 있을 때 dispose() 호출하여 사용
RxSwift 사용방법
1. 비동기로 생기는 데이터를 observable로 감싸서 리턴하는 방법
func downloadJson(_ url: String) -> /*나중에생기는데이터*/Observable<String?> {
//나중에생기는데이터를 만들어서 리턴해야함.
return /*나중에생기는데이터*/Observable.create() { f in
DispatchQueue.global().async { //계속 있어야함.
let url = URL(string: url)!
let data = try! Data(contentsOf: url)
let json = String(data: data, encoding: .utf8) //다른 스레드에서 실행되고 나서 메인스레드에 작업이 등록되어서 그다음에 아래 줄이 실행됨.
DispatchQueue.main.async {
// f(json)
//본체 함수가 끝나고 나서 나중에 실행됨. 그래서 escaping이라고 써줌. escaping으로 적어주면 수행하는 작업인 downloadJson 클로져 안의 코드들을(클로져) 수행. 만약에 escaping이 없으면 안함.
f.onNext(json) //f에 onNext라는 메소드를 통해서 전달 (next라는 스테이트를 주고 전달)
f.onCompleted() //순환참조 문제 해결
}
}
return Disposables.create()
}
2. Observable로 오는 데이터를 받아서 처리하는 방법
downloadJson(MEMBER_LIST_URL)
.debug() //윗줄과 아랫 줄 사이 데이터가 전달되는 동안 어떤 데이터가 전달되는지 확인가능.
.subscribe { [weak self] event in //event에는 next라는 이벤트가 옴
//순환참조 안생기게 하려면?
//순환참조 생기는 이유 클로져가 self를 캡쳐하면서 ref count가 증가하기때문에 생김. ref count가 다시 감소하게 하려면? 클로져가 생성되면서 ref count가 증가. 클로져가 없어지면 ref count가 감소됨.
//completed나 error에 클로져가 없어짐.
guard let `self` = self else { return }
switch event { //받을때도 next라는 status를 받아서 처리
case let .next(json):
DispatchQueue.main.async { //urlsession이 실행되는 다른 스레드에서 전달됨. UI 스레드가 아니라서 에러가 생김. 메인 스레드로 처리해줌.
self.editView.text = json
self.setVisibleWithAnimation(self.activityIndicator, false)
}
case .completed: break
//onCompleted가 호출되면 클로져가 스스로 사라짐
case .error: break
}
}
debug : 윗줄과 아랫 줄 사이 데이터가 전달되는 동안 어떤 데이터가 전달되는지 확인가능.
RxSwift에서의 순환참조 해결
let observable = downloadJson(MEMBER_LIST_URL)
_ = observable.subscribe { event in //클로져
switch event { //event에는 next라는 이벤트가 옴
case .next(let json):
self.editView.text = json
self.setVisibleWithAnimation(self.activityIndicator, false)
case .completed:
break
case .error(let error):
break
}
}
// observable로 오는 데이터를 처리하는 방법
// subscribe로 이벤트를 받아서 어떻게 처리할건지
- 위 클로져 안에서 순환참조가 발생함. (클로져는 Observable이 종료되면 사라짐.)
- 순환참조가 생기는 이유 : 클로져가 self를 캡쳐하면서 ref count가 증가하기 때문에 순환 참조가 발생함. ref count가 감소될때는 클로져가 없어질 때이다. (클로져가 생성되면서 ref count가 증가. 클로져가 없어지면 ref count가 감소됨.)
- Observable의 역할이 oncompleted/onError로 의해 기능이 끝나버리면 클로져도 종료가됨. 그리고 그 때 순환참조가 끊어질 기회가 있음.
- downloadJson(_ url: String) -> Observable<String?> 함수에서 onNext()이후에 onCompleted()를 추가해준다.
- onLoad() 함수에서 next 이벤트가 전달되고 난 후, completed 이벤트가 실행되고 클로져가 없어지며 self에 대한 ref count도 없어진다. (onCompleted가 호출되면 클로져가 스스로 사라짐)
- [weak self]를 사용하여 순환참조를 방지하는 방법도 있다.
Rxswift는 비동기적으로 생기는 데이터들을 리턴값으로 전달하기 위해서 나중에 생기는 데이터라는 클래스(Observable)로 감싸서 전달하기 위해 만들어졌다.
Observable의 생명주기
- Create: Observable이 create 되었다고 해서 모든 데이터가 생성 및 전달되지 않음. Subscribe가 되었을때 동작한다.
- Subscribe: subscribe가 붙었을때 Observable이 동작한다.
- onNext:
- 한번 만들어진 observable은 subscribe에 의해 동작이 실행되고, 그 동작이 시작되면 complete이나 error, dispose에 의해 동작이 끝난다..
- 동작이 끝난 observable은 다시 재사용을 못한다. (동작이 다 끝난 observable에 뭘 추가해봤자 동작 안됨. 다시 사용하고자 한다면 새로운 subscribe이 있어야 새로운 동작이 이루어짐.)
- onCompleted / onError
- Disposed
Operator (Sugar API)
just, from
return Observable.create() { emitter in //emitter는 데이터를 생성시키는 것
//데이터 전달
emitter.onNext("Hello world")
emitter.onCompleted()
return Disposables.create()
}
// MARK: - SUGAR API (just, from) -
// Optional로 감싸져서 프린트됨
return Observable.just("hello world")
//데이터 하나 보내서 리턴할때
return Observable.just(["hello"], ["world"])
return Observable.from(["hello", "world"])
// 각 배열 하나씩 보낼때
just : create해서 next로 데이터 전달하여 complete시킴. dispose했을때, 아무 일 없을 떼 씀.
subscribe
// MARK: - SUGAR API (Subscribe) -
_ = downloadJson(MEMBER_LIST_URL)
.subscribe(onNext: { print($0)},
onError: { err in print(err)},
onCompleted: { print("com")}) //onnext만 받아서 한줄로 처리
map, filter, observeOn, subscribeOn
// - SUGAR API 적용 -
_ = downloadJson(MEMBER_LIST_URL) // 생성: just, from
.map { json in json?.count ?? 0 } // operator : integer가 내려옴. 옵셔널이면 0
.filter { cnt in cnt > 0} // operator : integer가 0보다 큰 것만 필터링
.map { "\($0)"} // operator : integer를 string으로 바꿈.
.observeOn(MainScheduler.instance) //메인 스레드로 스레드 전환 (super:operator)
.subscribeOn(ConcurrentDispatchQueueScheduler(qos: .default))
//디폴트 qos를 가지는 디스패치큐 스레드에서 맨처음부터 실행됨. (메인 스레드에서 실행됨.)
//위치와 상관없음.
.subscribe(onNext: { json in
// DispatchQueue.main.async { // .observeOn(MainScheduler.instance)로 인해 삭제 가능
self.editView.text = json
self.setVisibleWithAnimation(self.activityIndicator, false)
// }
})
- map: 처음부터 쓸 수 있는게 아니라 observable가 있어야하고 그 다음 observable과 연결해줄 때 쓸 수 있음. observable과 observable 사이에 map operator를 쓸 수 있음. map에서 지정한 변환공식에 따라 바뀐 다음 아래로 전달. ex) map(x => 10*x)
- filter: 조건 범위 안에 한하여 동작
- observeOn: observeOn하고 스레드를 바꾸면 그 밑에줄부터 바꾼 스레드로 적용됨.
- 밑으로 내려가면서 영향을 준다. (Downstream의 스레드를 바꾼다.)
- .observeOn(MainScheduler.instance)로 인해 DispatchQueue의 구문을 없앨 수 있다.
- subscribeOn: 맨 첫째줄에 영향을 줌.
- 맨 위에 있는 것부터 적용 (위치와 상관없이 맨 처음 스레드의 시작을 지정함. Upstream의 스레드를 바꾼다.)
.observeOn(MainScheduler.instance)로 인해 삭제 가능
Operator : 데이터가 Observable에서 subscribe로 전달되는 중간에 받아서 데이터를 바꿔치는 역할을 한다.
반응형
'RxSwift' 카테고리의 다른 글
RxSwift in 4 hours 정리(2) (0) | 2021.03.18 |
---|