본문 바로가기
Swift/UIKit

[RxSwift]왜 비동기 처리가 안되는거지? viewDidLoad에서 onNext가 실행이 안될때... (부제: viewdidload에 disposableBag을 선언하면 생기는 일)

by 마라민초닭발로제 2023. 3. 23.

맨 밑에 3줄 요약 있습니둥

 

개요 : Alamofire로 데이터를 불러오고싶은데 onNext가 실행되지 않는다.

 

뭐가 잘못되었는지 하루종일 고쳐봤다...

 

override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .red
        makeConstraints()        
        
        let disposeBag = DisposeBag() //디스포스백 생성
        let temptemp = getData()
        temptemp.subscribe { data in
          	print("실행 돼?")
        }.disposed(by: disposeBag)
        
    }
    
    
    func getData() -> Observable<[PostElement]> {
            return Observable.create { emitter in
                let url = "https://jsonplaceholder.typicode.com/posts"
                
                AF.request(url,
                           method: .get,
                           encoding: JSONEncoding.default,
                           headers: ["Content-Type": "application/json", "Accept": "application/json"])
                    .responseDecodable(of: [PostElement].self) { response in
                        switch response.result {
                        case .success(let posts):
                            print("정상 동작?")
                            emitter.onNext(posts)
                        case .failure(let error):
                            emitter.onError(error)
                        }
                    }
                return Disposables.create()
            }
        }

viewdidload에서 정상 작동되지 않는다...  getData() 부분에서 .success가 돼 "정상동작" 부분에서는 정상 동작 했다. 그러면 생각해보면 observable의 생명주기에 따라 절대절대 onNext가 일어나고 print가 찍혀야 하는데 왜 안찍히는거지??

더보기

# observable의 생명주기

1. create

2. subscribe -> 이거 생성되었을때 비로소 작동을 시작함

3. onNext

4. onCompleted / on Error -> 여기서 끝나고

5. disposed

+이 글 적다보니 completed과 disposed가 애매해서 찾아봄

https://rhammer.tistory.com/286

 

RxSwift - Dispose란? (Disposable, DisposeBag)

Dispose and Terminating ~ P.66 Dispose and terminatingObservable은 subscribe가 있기 전까지 아무일도 하지 않는다는걸 떠올리자.Subscription이 있어야 비로소 Observable은 이벤트를 발생시키고 complete 또는 error이벤트

rhammer.tistory.com

메모리 Leak발생하니까 onCompleted 잊지 말고 쓰자고!

 

 

 

그러면 이것도 안되야 하는 것 아님?

 func getDataRXRX() -> Observable<String?> {
        return Observable.create { emitter in
            emitter.onNext("데이터 받아라")
            emitter.onNext("하이")
            emitter.onCompleted()
            return Disposables.create()
        }
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .red
        let disposeBag = DisposeBag()
        makeConstraints()
        _ = getDataRXRX().subscribe(
            onNext: { st in
            print(st)
        },
            onError: { err in
                print(err)
            },
            onCompleted: {
                print("getDataRXRX끝남")
            }
        ).disposed(by: disposeBag)
   	}

 

이건 또 정상적으로 실행 한다.

 

미치고 팔짝 뛸 노릇... 

 

해결 이유 : viewDidLoad()는 한번 지역 변수라 한번 실행하고 사라짐 -> 이말은 즉 한번 실행하고 사라지는 변수이다.

 

https://velog.io/@leeyoungwoozz/Swift-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B5%AC%EC%A1%B0

 

[Swift] 메모리 구조

WWDC Understanding Swift Performance 영상을 보다가, 이제껏 알고 있던 Swift 메모리 구조 관련 지식이 맞는지 확인하는 바 기록해본다!!프로세스가 실행되면 OS에서는 메모리 공간을 할당해주는데, 그 공

velog.io

 

https://medium.com/@vipandey54/uiviewcontroller-lifecycle-7ca2d36f4f07

 

UIViewController Lifecycle

In iOS each you can create views using storyboards, xibs or programmatically.  Independent from the approach, we need to understand when…

medium.com

라이프사이클에서 실행되는 것들은 결국 지역변수이다. 지역변수 이기 때문에, 비동기로 처리할 때 stack에서 viewdidload가 실행된 후 disposeBag은 사라지게 된다. 따라서 getData가 향한 disposeBag이 사라진 것이다.(AF함수는 Viewdidload에 선언되어도 그 자체가 데이터를 받아옴에 있어서 비동기적으로 실행한다. 완료 될 때 까지 viewdidload에서 안기다리고 다른 메모리 큐에서 실행 됨)

 

그래서 disposebag을 전역변수 ViewController내부에 설정하니 잘 동작하는 것을 확인할 수 있었다. 

 

let disposeBag = DisposeBag() // viewdidload가 아닌 전역변수로 설정

override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .red
        makeConstraints()
        let temptemp = getData()
        temptemp.subscribe(
            onNext: { data in
                print(data)
            },
            onCompleted: {
                print("끝남")
            }
        ).disposed(by: disposeBag)
    }
    func getData() -> Observable<[PostElement]> {
            return Observable.create { emitter in
                let url = "https://jsonplaceholder.typicode.com/posts"
                AF.request(url,
                           method: .get,
                           encoding: JSONEncoding.default,
                           headers: ["Content-Type": "application/json", "Accept": "application/json"])
                    .responseDecodable(of: [PostElement].self) { response in
                        switch response.result {
                        case .success(let posts):
                            print("정상 동작?")
                            emitter.onNext(posts)
                            emitter.onCompleted()
                        case .failure(let error):
                            emitter.onError(error)
                        }
                    }
                return Disposables.create()
            }
        }

이제 잘 작동 하는 것을 확인할 수 있었음.

 

 

그러면 아까 observable<String?>은 왜 안돼?? -> 그것은 viewdidload가 끝나기 전에 비동기 처리가 완료되었기 때문...

func getDataRXRX() -> Observable<String?> {
        return Observable.create { emitter in
            emitter.onNext("데이터 받아라")
            emitter.onNext("하이")
            emitter.onCompleted()
            return Disposables.create()
        }
    }
    let disposeBag = DisposeBag()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .red
        makeConstraints()
        let temptemp = getData()
        _ = getDataRXRX().subscribe(
            onNext: { st in
                print(st)
                print("데이터를 받자")
            },
            onError: { err in
                print(err)
            },
            onCompleted: {
                print("RX끝")
            }
        ).disposed(by: disposeBag)
        print("뷰디드로드 끝")
        
    }

 

다음과 같은 결과가 나왔음. 

 

만약 이것을

override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .red
        makeConstraints()
        let disposeBag = DisposeBag()
        let temptemp = getData()
        _ = getDataRXRX()//main말고 background 쓰레드에서 돌림
            .subscribe(on: ConcurrentDispatchQueueScheduler.init(qos: .background))
            .subscribe(
            onNext: { st in
                print(st)
                print("데이터를 받자")
            },
            onError: { err in
                print(err)
            },
            onCompleted: {
                print("RX끝")
            }
        ).disposed(by: disposeBag)
        print("뷰디드로드 끝")
    }
    
    func getDataRXRX() -> Observable<String?> {
        return Observable.create { emitter in
            emitter.onNext("데이터 받아라")
            sleep(1) // 1초 잠자기
            emitter.onNext("하이")
            emitter.onCompleted()
            return Disposables.create()
        }
    }

 

 

작업을 다른쓰레드에서 실행시킬 경우 viewdidload는 끝났는데 observer가 사라져서 옵저버 해제를 못한다. 

 

3줄요약

1. disposeBage은 전역변수에 선언 절대로 viewDidLoad같은 지역변수 영역에곳에 선언하지 않는다.

2. viewDidLoad도 함수라 lifecycle에 따라 heap에 있다가 메모리 해제되니까 잘 생각하면서 코드를 작성해야 한다.

3. 하루동안 고민했는데, 전혀 아깝지 않은 하루였다. (AF작동과 LifeCycle observer등 다양한 개념들을 심층적으로 배울 수 있었다.)