맨 밑에 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등 다양한 개념들을 심층적으로 배울 수 있었다.)
'Swift > UIKit' 카테고리의 다른 글
[UIKit] iOS 트러블 슈팅 UIStackView Distribution 이해하기 (0) | 2024.01.23 |
---|---|
[UIKit] because they have no common ancestor. Does the constraint or its anchors reference items in different view hierarchies? That's illegal Error 처리 (not addsubview) (0) | 2023.08.09 |
[UIKit] RxSwift로 tableview만들기 (0) | 2023.03.22 |
[UIkit] Tableview 코드로 만들기 (0) | 2023.03.21 |
[Swift] Icon 색과 크기 변경하는 방법 (0) | 2023.03.21 |