지금까지 순환참조는 안돼~ 메모리 릭 안돼~ 하면서 무작정 사용했던 weak self.
하지만 유튜버의 이 질문에 대한 영상을 보고 다 같아 붙일게 아니라, weak self 역시 어떻게 사용할지 고려하고 작성해야한다는걸 알았다.
그 내용을 정리해보자
이 유튜버의 영상을 보고 정리했습니다.
먼저 예제 코드.
class ViewModel {
func format(_ value: Int) -> String { return "number: \(value)"}
var handler: ((Int) -> Void)? = nil
func code() {
// 1.
let formatted = [1, 2, 3].map { [weak self] item in
return self?.format(item)
}
print(formatted)
// 2.
DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak self] in
let formatted = self?.format(42)
print(formatted as Any)
}
// 3.
handler = { [weak self] item in
let formatted = self?.format(item)
print(formatted as Any)
}
}
}
자 일단 1, 2, 3 번 예제들이 있다. 지금은 모두 weak self를 사용하고 있는데,
여기서 어떤 예제가 weak self가 필요없고, 필요할까?
먼저 1번부터 살펴보자
1번 예제
let formatted = [1, 2, 3].map { [weak self] item in
return self?.format(item)
}
사실 맵의 클로저에는 weak self를 사용해본적이 없는거 같다.
그래도 알아보자면!! 맵의 클로저에 weak self 사용? 결론부터 말하면 필요없다.
Map의 문서를 보면 내부적으로 클로저를 저장하고 있지 않다. (@escaping closure가 아니라는 뜻)
클로저가 무조건 실행이 끝난 후에야 함수도 종료되기 때문에 메모리 릭에 대해서 걱정할 필요가 없다.
2번 예제
DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak self] in
let formatted = self?.format(42)
print(formatted as Any)
}
보통 이 경우에도 무조건 weak self를 썼던거 같은데..
정답은 써도 되고 안써도 된다!
먼저 알아둘것은 비동기 함수를 호출해 실행할 클로저를 넘겨줄때는 클로저를 내부적으로 가지고 있는게 맞다.
그렇기에 당연히 메모리 릭이나, 크래시가 날 수 있다 라고 생각하는데...
DispatchQueue.global().async 의 클로저는 실행되면 자동으로 해제됨
그래서 써도 되고, 안 써도 되고 의 이유가 무엇이냐면!
iOS 개발할때의 기준으로 설명해보자
1. UI 업데이트를 비동기 클로저에서 하는 경우 ([weak self] 필요?)
클로저에서 UI 업데이트를 하기 전에 화면을 나가게 된다면, VC에 있던 UI는 이미 업데이트를 할 필요가 없는 상태다.
weak self를 안해줬다면, 화면을 나갔어도 남은 UI업데이트 처리가 끝난 후 deinit된다.
그럼 그냥 weak self 해버리는게 낫겠죠? 나가면 다른 작업 다 취소해뻐려! 니까
weak self를 해준다면, 화면을 나갔을 때 모두 deinit이 되고 클로저가 나중에 실행되어도 self 가 nil이라 별 동작 안하고 끝납니다.
2. 유저가 작성한 정보를 화면을 바로 나가더라도 서버에 꼭!!! 저장해야하는 경우 ([weak self] 안써도 됌)
유저가 작성한 정보를 무조건 서버에 저장 해야하는데, 유저가 작성 하고 저장 되기 전에 나가버린다면?
[weak self]를 사용한다면 저장이 안될것이다.
어차피 비동기 스레드에 사용하는 클로저는 실행되면 자동으로 해제된다. 그렇기에 weak self를 안쓰고 저장을 제대로 하고 종료되게 하는것이 좋겠죠?
아니면 [weak self] 쓰고 클로저 안에서 guard 문을 써서 언래핑 해서 사용한다거나.. 조금 더 맘에 드는 방식을 쓰면 될 듯 합니다.
어차피 weak self를 쓰든 안쓰든 클로저가 끝나면 해제 되니까!
그래도 고려해서 쓰기를 해보자
추가로 GCD 클로저 말고도 animation의 클로저도 따로 프로퍼티에 저장하지 않는 한 메모리릭이 발생할 일은 없슴다.
3번 예제
handler = { [weak self] item in
let formatted = self?.format(item)
print(formatted as Any)
}
이건 위에 전체 예제코드에서 봤듯이, handler라는 클래스 프로퍼티에 클로저를 저장하고 있다.
여기에는 weak self를 꼭! 꼭! 써야한다.
아니면 순환참조가 일어나니까!
클로저가 클래스를 self로 가지고 있는데, 이걸 또 클래스가 프로퍼티로 클로저를 가지고 있다..
이건 화면을 나가서 클래스가 해제되더라도 클로저 때문에 참조 카운트가 살아있는 문제가 발생한다.
순환 참조에 대한 자세한 이야기는.. https://justhm.tistory.com/78
ARC (자동 참조 카운트) 자세히 알아보기 + 참조 키워드(weak, strong, unowned)
정의ARC(Automatic Reference Counting)는 메모리 구조에서 힙 영역을 관리한다.Swift에서 힙에 메모리를 할때는 참조타입을 생성할 때 자동으로 할당하게 된다.ARC는 할당된 메모리가 더 이상 필요하지 않
justhm.tistory.com