⚠️ 순환 참조 생성시 발견한 사소한(?) 오류
var a: A?
var b: B?
a = A(name: "personA", home: nil)
b = B(name: "apartmentB", tenant: nil)
a?.home = b
b?.tenant = a
a = nil
b = nil
→ 의도한 대로 순환참조 발생
var a: A? = A(name: "personA", home: b)
var b: B? = B(name: "B", tenant: a)
a = nil
b = nil
→ a와 b 모두 메모리에서 해제됨
🧐 둘다 동일한 방법으로 생성한 객체인데 왜 아래 방법은 메모리에서 해제되는가?
➡︎ ❗️ 두 번째 방법은 애초에 잘못된 코드였음!!
var a: A? = A(name: "personA", home: b)
→ a를 생성하는 시점에 b는 없음. 그래서 원래는 여기에서 오류가 나야하는데,, 탑레벨에서는 이유는 모르겠지만 오류가 나지않고 빌드가 됨... (함수 내부에 넣어보면 오류 발생)
이때 a.home을 찍어보면 nil값으로 출력
즉, 애초에 a는 b를 참조하고 있지 않았던 것!
다음으로 생성된 b에서는 a를 참조하지만 a가 nil값으로 먼저 해제되기 때문에 a와 b 사이에 순환 참조가 발생하지 않았음.
⚠️ 순환 참조 생성시 있었던 이상한 점..
클로저의 순환 참조를 만들고 있었는데 제대로 구현한 것 같은데 또 메모리에서 해제가 됨...
var a: A?
var b: B?
func circularRef() {
a = A(name: "personA", home: nil)
b = B(name: "apartmentB", tenant: nil)
a?.home = b
b?.tenant = a
b?.closure = {
print("\(a?.name)")
}
}
circularRef()
a = nil
b = nil
첫 번째 오류를 피하기 위해 인스턴스 선언과 nil 할당 외에는 함수 내에서 구현하였는데, 순환 참조가 생성되지 않고 계속 메모리에서 해제됨.
nil할당을 없애고 코드 내에서 인스턴스를 선언 하였더니 순환 참조가 생성되었음.
func circularRef() {
var a: A?
var b: B?
...
}
circularRef()
→ 함수 내에서 인스턴스를 선언했기 때문에 함수의 실행이 종료되면 자동적으로 두 인스턴스가 사라짐. 따라서 따로 nil값을 할당해줄 필요가 없음.
이렇게 해결하긴 했지만.. 튜터님 말씀으로는 탑레벨에서 인스턴스에 직접 nil값을 할당해줄 때 컴파일러 내부에서 ARC에 관한 동작이 우리가 생각하는 것과는 다르게 흘러가는 듯하다고 합니다.
⇒ 탑레벨에서 전역변수로 선언될 때는! a가 아니라 (예시)Global이라는 전역변수가 담긴 무언가를 참조함! (실제로는 어디에 담기는지는 모르겠지만) 그래서 a 인스턴스 자체에는 참조 카운트가 올라가지 않기 때문에 a = nil을 할당해주었을 때 메모리에서 해제됨!!
var a: A?
var b: B?
func circularRef() {
a = A(name: "personA", home: nil)
b = B(name: "apartmentB", tenant: nil)
a?.home = b
b?.tenant = a
b?.closure = { [a] in // a를 참조하도록 명시
print("\(a?.name)")
}
}
➡︎ 그래서 이렇게 클로저 내에서 [a]를 추가하여 a 인스턴스를 참조하도록 직접 명시하면 순환 참조가 발생
getter를 호출var realName: String = "이름"
var name: String {
get { realName }
set { realName = newValue }
}
print(name) // name의 getter를 호출 == realName을 불러옴
name = "바보" // name의 setter를 호출 == realName에 newValue인 "바보"를 할당
→ 연산 프로퍼티는 값을 직접 저장하지 않음!
protocol Greeting {
var to: String { get }
var greeting: String { get set }
}
{ get }
: 구현의 자유도가 높음 (var, let, private var, 연산 프로퍼티 모두 가능)
: 프로토콜에서의 '최소 제약사항'이므로 채택 시 { get set }으로도 구현 가능
{ get set }
: 구현 자유도가 낮음
→ set을 쓰면 외부에서 구현이 가능해야함 == 상수와 private var는 사용 불가 (값을 변경하지 못하기 때문)
💡
var는 setter 없이도 왜 사용 가능한지?
→ 저장 프로퍼티(var)는 내부적으로 getter와 setter를 구현해둔다고 함
→let은 setter가 없음
→private var는 외부에서 set을 해서는 안됨