✅ deinit
메모리 상에서 뷰컨이 필요 없어져서 메모리 상에서 사라졌는지 확인할 때 사용
deinit {
print("deinit", self)
}
메모리 누수를 찾아볼 수 있는 방법이다.
back button 클릭하여 뷰가 사라질 때 deinit 호출 됨
viewcontroller에 작성했던 collection view의 설정들을 view 파일에서 작성하기
🚨view에서 화면 전환이 불가능
화면 전환 관련 코드들은 viewController가 가지고 있기 때문에 view에서 작성 시 didSelectItem에서 화면 전환을 구현할 수 없다.
값 전달 활용 - protocol
protocol을 활용하여 셀을 선택 함을 알려줌
1. protocol 생성
2. delegate 생성
3. 셀 선택 시 viewcontroller에게 명령 보내기
4. 프로토콜 호출 후 delegate 연결
🚨 deinit이 호출되지 않는다.
프로토콜 호출 한 후에 셀 선택하여 pop을 수행할 때 deinit이 호출되지 않는다.
deinit이 호출되지 않는다면 화면 전환할 때 마다 메모리에서 사라지지 않고 쌓이게 된다.
💡 weak, AnyObject
⇒ deinit 정상 작동
AnyObject
클래스에서만 프로토콜을 정의할 수 있도록 제약
해당 프로토콜은 클래스에서만 사용할 수 있다!
⇒ weak를 사용해야 하기 때문에,,
weak
강한 순환 참조가 걸려있기 때문에 weak 사용
weak로 선언하게 되면, 인스턴스 참조가 발생하더라도 RC +1을 하지 않는다.
➡️ 약한 참조
💡 Any vs AnyObject
모두 여러 타입에 대해 대응할 수 있다.
어떤 타입으로 된 요소를 가지고 있는지 컴파일 시점에서는 알 수 없고, 런타임 시 타입이 결정되기 때문에 값에 접근할 때 타입캐스팅을 해야한다.
✅ Any
모든 타입에 대한 인스턴스 대응 가능 (구조체, 열거형, 클래스, 클로저 등)
✅ AnyObject
모든 클래스 타입의 인스턴스만 담을 수 있음 ⇒ 클래스 제약 설정 가능 : TypeAlias
✔️ typealias
Void도 typealias
typealias Void = ()
AnyObject는 클래스 타입만 사용 가능하기 때문에 열거형일지 구조체일지 타입을 준비할 필요가 없다. → 효율적
AnyObject 타입의 프로토콜은 struct에서 사용 시 오류 발생!
❓ AnyObject 타입이 아닌 프로토콜을 호출한다면
weak는 클래스에서만 사용할 수 있기 때문에 오류가 발생한다.
💡 ARC
Heap에는 클래스의 인스턴스, 클로저 등을 저장한다. Heap에 저장되는 메모리는 직접 할당하고 해제할 수 있다.
ARC → Automatic Reference Count
ARC는 메모리를 추적하고 관리하는 시스템으로, 필요하지 않은 인스턴스가 있다면 자동으로 메모리에서 해제하는 역할을 담당한다.
인스턴스를 참조하는 횟수만큼 RC를 증가시키고, 인스턴스가 해제될 때 감소시킨다.
더이상 인스턴스를 참조하고 있지 않은 경우(RC → 0) 메모리에서 해제시킨다.(deinit 호출)
ARC 등장 전 까지는 MRC 사용
- MRC → 개발자가 수동으로 rc 관리를 해야했다. rc값을 런타임 시점에 관리
- ARC → 개발자가 관리할 필요가 없다. 컴파일 시점에 인스턴스 관리한다.
💡Memory Leak
메모리에서 해제되지 못하고 떠돌아 다니는 상태
인스턴스가 서로 참조하는 순환 참조 상태일 때 메모리 누수 현상이 발생한다.
✅ String Reference Cycle 강한 순환 참조
인스턴스 생성 후 서로 참조하는 상태
RC가 2인 상태에서 nil로 메모리 해제하면 -1이 되기 때문에 남은 RC가 각각 1이 된다.
메모리는 해제 하였지만 서로의 인스턴스를 바라보고 있기 때문에 메모리에서 완전히 해제되지 않는다.
→ deinit이 호출되지 않음
➡️ 순환 참조 발생!!
인스턴스의 참조 관계를 끊어 해결할 수 있지만 수동으로 모든 것을 해결하기엔 굉장히 번거로움
💡weak, unowned
순환참조가 발생하지 않도록 하는 키워드
✅ weak
weak로 선언하게 되면, 인스턴스 참조가 발생하더라도 RC +1을 하지 않는다.
→ 정상적으로 deinit이 된다.
weak var로 선언하고 옵셔널 설정도 해줘야 한다.
weak로 선언할 경우 런타임에서 nil로 값이 변경될 가능성이 있기 때문에 항상 변수로 선언한다.
nil로 변경될 가능성이 있다면, 옵셔널 타입이어야 하기 때문에 항상 옵셔널로 선언한다.
✔️ weak 사용 시 메모리를 해제하는 순서
순환 참조하고 있는 두 인스턴스 중 특정 인스턴스에 대한 참조만 해제될 경우 메모리에서 해제되지 않을 가능성이 있다!
➡️ 따라서 다른 인스턴스 메모리를 먼저 해제할 수 있는 경우 약한 참조를 사용 해야한다. → 수명이 짧은 인스턴스
✅ unowned
weak와 동일한 특성을 가지고 있다.
✔️ weak와의 차이점
메모리에 대한 주소값을 가지고 있음
참조하고 있는 인스턴스가 메모리에서 해제되어도 자동으로 참조된 인스턴스의 메모리를 nil로 변경시켜주지 않는다.
→ 메모리에 인스턴스가 사라졌다고 해도 메모리 주소값을 계속해서 가지고 있다
➡️ 인스턴스의 수명이 다른 인스턴스와 동일하거나 더 긴 경우에 unowned를 사용한다.
🚨 인스턴스 접근하려고 하면 주소값은 있는데 인스턴스는 없어서 값을 찾지 못해 런타임 오류가 발생한다.
✅ delegate는 weak로 선언하자
✔️weak로 선언하지 않으면 RC에 의해 메모리에 인스턴스가 제거되지 않는 문제가 발생한다.
- 때문에 weak를 이용하여 약한 참조 관계를 만들어줘야 한다.
✔️ weak로 delegate를 선언하게 되면 오류가 발생한다.
- 인스턴스를 참조한다는 것은 레퍼런스 참조인데 구조체는 값 타입이고, 클래스는 레퍼런스 타입
- 때문에 프로토콜을 ‘참조’하겠다는 것은 클래스 타입으로 채택하여 사용하겠다는 뜻
- 그렇다면 프로토콜을 AnyObject로 클래스 제약을 걸어주어야 참조가 가능하다.
💡 Closure
user = nil
로 해제를 하였지만 함수, 변수의 생명주기 때문에 result를 통해 해제된 데이터가 출력이 가능하다
해제된 후 nickname은 어떻게 가져오나?
캡쳐를 해둔다!
➡️ 클로저 캡쳐리스트, context
✅ Reference Capture 참조 캡쳐
내부의 클로저에서 클로저 외부에서 선언된 변수를 사용할 경우 내부적으로 변수를 저장하여 사용하는데, 이를 캡쳐되었다고 표현한다.
클로저는 값 타입인지 참조 타입인지 상관 없이 Reference Capture를 한다.
→ 클로저 외부에 선언된 total 변수 값이 캡쳐되어 초기화되지 않음
✅ 값 타입 캡쳐
참조 관계에서 벗어나려면 값 타입으로 캡쳐가 필요하다. → 값을 복사하는 것!
클로저 선언 시점에 대한 값을 값 타입으로 캡쳐할 수 있다.
→ [number]와 같은 형태로 명시
값 타입으로 캡쳐가 될 경우, 상수 형태로 캡쳐가 되기 때문에 클로저 내부에서 값 변경이 불가능하다!
🔎 Closure Strong Reference Cycle
클로저 함수 내에서 self 키워드를 사용하게 되면 rc값이 증가하게 된다.
rc값을 증가시키지 않으려면 [weak self] 사용
Animation
func setAnimation() {
// 시작
sampleView.alpha = 0
greenView.alpha = 0
// 끝
UIView.animate(withDuration: 1, delay: 2, options: .curveLinear) {
self.sampleView.alpha = 1
self.sampleView.backgroundColor = .blue
} completion: { bool in
UIView.animate(withDuration: 1) {
self.greenView.alpha = 1
}
}
}
사용자가 클릭에 대한 상호작용을 주고 싶을 때 사용한다.
'iOS > 🌱 SeSAC' 카테고리의 다른 글
23.10.31 화 (0) | 2023.11.01 |
---|---|
23.09.01 금 (0) | 2023.09.01 |
23.08.30 수 (0) | 2023.09.01 |
23.08.29 화 (0) | 2023.08.31 |
23.08.28 월 (0) | 2023.08.31 |