// Value Type
var num1 = 10
var num2 = num1
num2 = 15
print(num1, num2)
/* print
* 10, 15
*/
// Reference Type
class Sample {
var num1: Int
var num2: Int
init(num1: Int, num2: Int) {
self.num1 = num1
self.num2 = num2
}
}
var sample1 = Sample(num1: 10, num2: 20) // Sample 인스턴스 생성, sample1이 참조함
var sample2 = sample1 // sample1이 참조하는 인스턴스를 참조
var sample3 = sample2 // sample2가 참조하는 인스턴스를 참조
var sample4 = Sample(num1: 100, num2: 200) // Sample 인스턴스 생성. sample1과 다른 인스턴스
print(sample1.num1, sample1.num2)
print(sample2.num1, sample2.num2)
print(sample3.num1, sample3.num2)
print(sample3.num1, sample3.num2)
/* print
* 10, 20
* 10, 20
* 10, 20
* 100, 200
*/
// sample3에서 변경한 프로퍼티가 sample1과 sample2에 영향을 줌
sample3.num1 = 30
sample3.num2 = 40
print(sample1.num1, sample1.num2)
print(sample2.num1, sample2.num2)
print(sample3.num1, sample3.num2)
print(sample3.num1, sample3.num2)
/* print
* 30, 40
* 30, 40
* 30, 40
* 100, 200
*/
let sample = Sample() // 생성된 instance를 상수 sample이 참조하면서 count + 1
nil
을 할당하면 메모리에서 해제된다.var sample1: Sample? = Sample1() // Sample1 ref 1
func execute() {
let sample2 = sample1 // Sample1 ref 2
let sample3 = Sample3() // Sample3 ref 1
}
execute() // Sample1 ref 1, Sample3 ref 0(release).
sample1 = nil // Sample1 ref 0(release)
/* print */
// Sample3 is deinitialized
// Sample1 is deinitialized
class Sample1 {
var sample2: Sample2?
}
var sample1: Sample1? = Sample1() // Sample1 ref 1
sample1?.sample2 = Sample2() // Sample2 ref 1
sample1 = nil // Sample1 ref 0(release) -> Sample2 ref 0(release)
/* print */
// Sample1 is deinitialized
// Sample2 is deinitialized
class Sample1 {
var ref2: Sample2?
}
class Sample2 {
var ref1: Sample1?
}
var sample1: Sample1? = Sample1() // Sample1 ref 1
var sample2: Sample2? = Sample2() // Sample2 ref 1
sample1?.sample2 = sample2 // Sample2 ref 2
sample2?.sample1 = sample1 // Sample1 ref 2
sample1 = nil // Sample1 ref 1
sample2 = nil // Sample2 ref 1
sample1
과 sample2
에 nil
을 할당하면서 각각 인스턴스 참조 횟수가 감소했지만, 프로퍼티 ref1
, ref2
가 sample1
과 sample2
를 할당받으면서 증가시킨 참조 횟수가 아직 남아있다.nil
을 할당해서 해제하려고 해도 sample1
과 sample2
에 nil
을 할당했기 때문에 ref1
과 ref2
에 접근할 수 없다. 두 instance는 참조 횟수가 절대로 0이 될 수 없기 때문에 영원히 메모리에 남아있게 된다.ref1
과 ref2
에 먼저 nil
을 넣어서 해제시킨 뒤에 sample1
과 sample2
를 해제시켜도 되지만, 애초에 ref1
과 ref2
가 instance를 참조할 때 참조 횟수가 증가하지 않았다면 위와 같은 간결한 코드로 해결이 가능하다.nil
을 할당한다. 아직 instance가 참조되고 있는데 참조 횟수가 0이라서 메모리에서 해제되는 상황이 발생할 수 있다. 이 때, 잘못된 메모리에 접근하는 오류가 발생할 가능성이 있으므로 nil
을 할당해서 오류를 막는다.ref2
를 약한 참조 옵션으로 설정하면 sample2
에 nil
을 할당할 때 Sample2 instance의 참조 횟수가 0이 되어 참조 순환이 깨지고 양 쪽의 instance가 정상적으로 메모리에서 해제된다.ref1
도 메모리에서 해제되면서 Sample1의 참조 횟수도 감소시킨다.class Sample2 {
var sample1: Sample1?
}
var sample1: Sample1? = Sample1(value: 10) // Sample1 ref 1
var sample2: Sample2? = Sample2(value: 20) // Sample2 ref 1
sample1?.sample2 = sample2 // Sample2 ref 1
sample2?.sample1 = sample1 // Sample1 ref 2
sample1 = nil // Sample1 ref 1, Sample2 ref 1
sample2 = nil // Sample2 ref 0, Sample1 ref 0
/* print
self
를 이용해 접근하는데, 이 떄 클로저의 값 획득에 의해 instance를 참조한다.self
를 사용하면 상호 참조 관계가 성립된다.class Sample {
var value = 10
lazy var closure: () -> Int = { // Sample ref 1, Closure ref 1
self.value = 20
return self.value
}
}
var sample: Sample? = Sample() // Sample ref 2
print(sample?.closure())
sample = nil // Sample ref 1
self
를 참조해 두고 사용한다.(self
를 여러번 사용해도 참조 횟수는 한 번만 증가한다.)self
에 대해 참조 카운팅 옵션을 사용해서 문제를 해결할 수 있다.self
의 참조 획득 방식을 약한 참조(weak)로 설정하면 클로저가 instance를 약한 참조하게 되고 상호 참조 관계를 깨트릴 수 있다.class Sample {
var value = 10
lazy var closure: () -> Int = { [weak self] in // Sample ref 0, Closure ref 1
guard let `self` = self else { // 약한 참조로 설정하면 옵셔널 타입으로 획득
return -1
}
self.value = 20
return self.value
}
}
var sample: Sample? = Sample() // Sample ref 1
print(sample?.closure())
sample = nil // Sample ref 0
/* print */
// Sample is deinitialized
Introduce - UIView의 좌표(origin) 및 크기(size)를 나타내는 속성에는 frame, bounds가 있다. - 둘의 차이는 어디를 기준으로 그려지는가에 있다. Reference [iOS Swift] 영역 크기와 위치 - Frame / Bounds iOS) Frame과 Bounds의 차이(1/2) [ios] Bounds vs ...
스토리보드를 통해 static cell을 갖는 UITableView를 만들 때, UITableViewController를 subclassing하지 않으면 컴파일 오류가 발생합니다. Static cell tableview는 반드시 UITableViewController를 상속받은 controller를 통해 생성되어야 하기 때문입니다. 그래서 static ce...
Introduce - 클로저를 함수 또는 메소드의 전달 인자로 사용할 때, @escaping과 @autoclosure 속성을 적용할 수 있다. Escaping - 함수 인자로 전달된 클로저는 기본적으로 비탈출 클로저이다. - 클로저의 탈출이란 함수 인자로 전달된 클로저가 함수 실행 종료 이후에 실행될 수 있는 것을 말한다. - 클로저가 탈출하면 함수...
Introduce - 클로저가 주변 context를 통해 상수 및 변수 값을 클로저 내부에서 사용하면, 그 값을 그대로 사용하지 않고 별도의 참조를 획득해서 사용한다. - Context는 클로저 내부 코드가 실행되기 위해 필요한 환경을 의미한다. Value Capture - 클로저 외부 값이 더 이상 존재하지 않더라도 항상 클로저를 실행시킬 수 있도...
Introduce - Swift 및 Objective-C에서 참조 메모리 관리를 자동으로 해 주는 기능 - 인스턴스의 참조횟수를 추적해서 더 이상 참조되지 않는 인스턴스를 메모리에서 해제시킴 Why use? - 값 타입(struct, enum, literal 등)은 변수, 상수, 프로퍼티 등에 할당될 때 값을 복사하기 때문에, 할당된 값을 변경해도 ...