오늘은 zeroing과 객체 생명주기에 대해서 알아보자.
zeroing은 ARC에서 스치듯 지나갔다.
Zeroing
을 이해하기 위해서는 weak
에 대해서 알고 있어야한다.
weak
의 특징을 간단하게 알아보자.
weak
는 reference counting을 증가시키지 않는다.weak
는 nil이 할당될 수 있어 var와 Optional을 사용해야한다.weak으로 선언하면 참조하고 있는 것이 먼저 메모리에서 해제되기 때문에
ARC는 weak으로 선언된 참조 대상이 해제되면 런타임에 자동으로 참조하고 있는 변수에 nil을 할당한다.
(ARC에서 약한 참조에 nil을 할당하면 프로퍼티 옵저버는 실행되지 않는다.)
Zeroing
은 위에서 말하는 참조 대상이 해제되며 런타임에 자동으로 참조하고 있는 변수에 nil을 할당하는 과정을 뜻한다!
Side Table에 대해서는 ARC에서 약한 참조에 nil을 할당하면 프로퍼티 옵저버는 실행되지 않는다의 이유를 찾다 여기까지 와버렸다...ㅋㅋㅋ
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
}
deinit{
print("\(name) is deinit")
}
}
class Apartment {
let unit: String
weak var tenant: Person? = nil {
didSet {
print("change")
}
}
init(unit: String) {
self.unit = unit
}
deinit{
print("\(unit) is deinit")
}
}
var John: Person?
var unit4A: Apartment?
John = Person(name: "John")
unit4A = Apartment(unit: "4A")
John!.apartment = unit4A
unit4A!.tenant = John
John = nil
unit4A = nil
우선 코드를 봐보자.
Apartment의 tenant 프로퍼티에 옵저버를 붙였다.
didSet
옵저버를 붙였으니 tenant 프로퍼티의 값이 변할때마다 "change"가 출력되야 한다.
그렇다면 언제 옵저버가 작동할까?
Apartment 클래스 인스턴스인 unit4A의 tenant가 변할때일 것이다.
unit4A!.tenant = John
에서 한 번 "change"가 출력될 것이고,
John = nil
이후에는 출력될까?
Apartment의 tenant 프로퍼티는 weak
참조로 해당 프로퍼티가 참조하고 객체가 메모리에서 해제되면 zeroing을 통해 nil
이 반환되어 저장될 것이다.
그렇다면, John = nil
코드 이후에는 unit4A의 tenant 프로퍼티는 nil
이 될 텐데 옵저버가 실행되어야하지 않을까?
change
John is deinit
4A is deinit
ARC에 의해 nil이 지정되는 경우에는 프로퍼티 옵저버가 실행되지 않는다.
그렇다면 이유가 있을거 같은데 공식문서에도
이렇게만 나와있다...!ㅋㅋㅋ
(너무 불친절해...ㅋㅋㅋㅋ)
그래서 이리저리 알아보다 Side Table과 Object Life Cycle에 대해 정리해보려 한다.
ARC를 공부하면서 Reference Count는 어떻게 관리될까에 대해 알아봤었다.
클래스 인스턴스가 생성되면 HeapObject가 생성되는데 이때 HeapObject에는 인스턴스의 정보를 포함해 RC도 같이 저장된다고 했다.
그래서 HeapObject에 대해서 한 번 봐봤다!
Reference Count를 어떻게 관리하는지 볼 수 있는 Swift Code이다.
HeapObject가 어떻게 구성되어 있는지 확인할 수 있다.
HeapObject 구성을 보면 InlineRefCountBits
에는 strong RC + unowned RC + flags / HeapObjectSideTableEntry 라고 써있다.
Inline이라고 하니 HeapObject 내부에 구성되어 있을거 같다.
InlineRefCountBits
에는 strong
, unowned
는 있는데 weak
은 없다..
그렇다면 HeapObjectSideTableEntry에 있나 확인해보자.
HeapObjectSideTableEntry에는 weak RC
가 있다.
흠... 또 pointer를 가지고 있다.
weak RC
는 object에 대한 약한 참조를 계산한다.
object 할당이 해제된 후, weak RC
가 0이 되면 object의 side Table entry가 해제된다.
처음 Object에는 Side Table이 없다.
그렇다면 언제 Object에 Side Table이 생길까?
객체가 weak
참조될 때 혹은 strong
이나 unowned
RC가 오버플로될때 생긴다고 한다.
Strong and unowned variables point at the object.
Weak variables point at the object's side table.
이 부분이 가장 흥미로웠다.
strong
, unowned
는 object를 가리킨다.
weak
는 side table을 가리킨다.
이런 그림으로 설명할 수 있을거 같다.
그럼 이제 다시 내가 궁금해하던거에 대해서 생각해보자.
ARC에서 약한 참조에 nil을 할당하면 프로퍼티 옵저버는 실행되지 않는다.
weak
참조는 Object를 가리키지 않고, side table을 가리키고 있기 때문에 ARC에 의해서 Object가 해제되면 weak
입장에서는 옵저버를 실행할 수 없다고 생각했다.
옵저버와 관련된 내용은 Object 정보에 들어있을테니..!
내가 추측한 바로는 그런데 혹시 틀리거나 다르게 생각하면 언제든 의견을 남겨주면 좋겠다!
Object의 생명 주기는 위와 같다고 한다.
하나씩 알아보자!
LIVE
상태는 object가 살아있는 상태이다.
weak
참조가 없다면 side table이 없다.
strong RC
가 0이되면 deinit()
을 호출하고 object는 DEINITING
상태가 된다.
Object에서 deinit()
이 진행되고 있는 중이다.
unowned
변수는 swift_abortRetainUnowned()
에서 load가 중단된다.
unowned
변수 저장소는 정상적으로 작동한다.
ARC 포스팅에서 unowned
는 nil
을 할당할 수 없고, 항상 Object가 메모리에 올라와 있다고 생각하고 사용한다고 했다.(unowned Optional이 아닌 경우)
따라서 Object가 deinit()
되면 swift_abortRetainUnowned()
를 통해 Object에 대한 load는 중단하지만 nil
이 아니므로 저장소는 그대로인거 같다.
weak
변수 저장소에는 nil
을 저장한다.
deinit()
이 완료되면 swift_deallocObject
를 호출한다.
swift_deallocObject
는 canBeFreedNow()
를 호출해 빠르게 weak
와 unowned
참조가 없는지 확인한다.
canBeFreedNow()
의 결과값이 true
라면 Object는 FREED
를 거쳐 DEAD
된다.
canBeFreedNow()
의 결과값이 false
라면 unowned RC
를 감소시키고 Object는 DEINITED
상태가 된다.
weak
변수에 nil
을 반환한다.
weak
변수 저장소는 nil
을 저장한다.
canBeFreedNow()
는 항상 false이므로 DEAD
로 전환되지 않는다.
나머지는 위의 DEINITING
과정과 동일하다.
deinit()
은 완료되었지만 unowned
참조는 처리되지 않은 상태이다.
strong
참조는 이미 처리되어 있는 상태이다.
unowned
변수는 swift_abortRetainUnowned()
에서 load 중지된다.
side table이 없는 경우이므로 weak
참조가 없는 상태이다.
그러므로 unowned RC
가 0이 되면 Object가 해제되고 DEAD
된다.
weak
변수에 nil
을 반환한다.
unowned RC
가 0이 되면, 그 Object는 아직 weak
참조가 refs가 있으므로 FREED
상태가 된다.
나머지는 위의 DEINITED
과정과 동일하다.
FREED
상태는 DEINITED
상태에서 weak
참조 refs가 남아있는 경우에 진입 하므로 이 상태는 절대 일어날 수 없다.
Object는 해제되었지만 side table에 대한 weak
참조가 남아있는 상태이다.
(weak
참조는 Object를 가리키지 않고, side table을 가리키고 있으므로)
weak
참조가 0이 되면 side table이 해제되고 Object가 DEAD
상태가 된다.
Object와 side table이 모두 없어진 상태이다.
오늘은 Zeroing과 Side Table과 Object Life Cycle에 대해서 알아봤다.
한 3일동안 ARC에 대해서 알아봤다.... 힘들군ㅋㅋㅋ
그럼 이만👋
크 잘봤습니다🙋🏻♂️