ARC, Weak, Strong, Unowned

lsj16632·2022년 7월 14일
0

iOS 면접 준비

목록 보기
3/6
post-thumbnail

1. ARC(Automatic Reference Counting)

스위프트는 ARC를 사용하여 앱의 메모리 사용량을 추적하고 관리한다. ARC는 해당 인스턴스가 더 이상 필요하지 않을 때 클래스 인스턴스에서 사용하는 메모리를 자동으로 해제한다. 클래스 인스턴스가 더이상 필요하지 않을때 해당 메모리를 자동으로 비워준다. 참조 될때마다 참조횟수가 +1이 되고 nil을 할당해주면 참조 횟수가 -1이 된다.

ARC의 메모리 수거 대상으로는 참조타입인 클래스에만 적용되고 값 타입인 구조체나 열거형 등은 수거 대상에서 제외된다.

class Person {
    let name: String
    init(name: String) {
        self.name = name
        print("\(name) is being initialized")
    }
    deinit {
        print("\(name) is being deinitialized")
    }
}

var p1: Person? = Person(name: SJ)
var p2: Person? = p1
var p3: Person? = p2

p1 = nil
p2 = nil

init은 초기화 함수이고 deinit함수는 클래스 인스턴스의 메모리가 해제되었을 때 호출되는 함수이다. p1이라는 인스턴스 변수를 생성하여 초기화 하고, p2 변수에 p1을 할당, p3에 p2를 할당했다. 즉 p1과 p2 p3에 Person 인스턴스가 할당된 것(강한참조==Strong)이다. 그러면 Reference count는 각각 1이기 때문에 ARC는 이 메모리를 해제하지 않는다.

이후 실험을 위해 p1과 p2에 nil을 할당하였다. 결과는? ...

생성만 되었다.🤔 이유는 아직 p3에서 참조하고 있기 때문!!

p3 = nil

위 코드에 p3에 nil을 할당하는 것을 추가하고 다시 실행해보았다.
결과는 드디어 소멸되었다.

결론은 ARC는 인스턴스에 대한 강한참조가 남아있으면 절대 메모리에서 해제하지 않는다❗️

즉 이말은 안좋게 보면 메모리에서 해제되지 않는다는 건 메모리 누수로 이어질 수 있다는 것이다...👎🏻Shit...


2. Strong(강한 참조), Weak(약한 참조), Unowned(소유 되지 않은 참조)

내가 iOS 앱 개발을 공부하면서 Weak와 Strong 키워드를 보게 된 것은 텍스트 필드나 라벨 테이블 뷰 등을 아울렛 변수로 연결할 때이다.

@IBOutlet weak var tableView: UITableView! 

처음엔 이게 뭔가 별로 중요하지 않겠구나 하고 넘어갔는데 메모리 참조와 관련된 면접에서도 단골로 나오는 내용이었다.

2 - 1 Strong(강한 참조)

우선 강한참조(Strong)란 Reference count를 증가시키는 것이다. 이 키워드를 사용하는 경우는 위에 ARC의 예시코드에서 보았던

	var p1 = Person(name: "sj")

클래스의 인스턴스를 할당하는 경우인데 이를 통해 ARC의 메모리 해제를 피하고, 객체를 안전하게 사용하고자 할 때 사용❗️한다.

class Person {
    let name: String
    init(name: String) { self.name = name }
    var apartment: Apartment?
    deinit { print("\(name) is being deinitialized") }
}

class Apartment {
    let unit: String
    init(unit: String) { self.unit = unit }
    var tenant: Person?
    deinit { print("Apartment \(unit) is being deinitialized") }
}

var john: Person?
var unit4A: Apartment?

john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")

// 여기 주의 깊게 볼것❗️
john!.apartment = unit4A
unit4A!.tenant = john

다음 코드의 참조관계는 우선 Person 인스턴스를 할당받은 john변수는 Person 인스턴스를 강하게 참조하고 Apartment 인스턴스를 할당받은 unit4A또한 Apartment 인스턴스를 강하게 참조한다.

주의 깊게 봐야 할 것은 Person 클래스 안에 있는 apartment 프로퍼티의 타입이 Apartment타입이라는 점Apartment 클래스 안에 있는 tenant이 Person타입이라는 점이다. 집이 있으면 거주자가 있기 때문에 서로 강하게 참조한다.

john = nil
unit4A = nil

이렇게 john과 unit4A에 nil을 할당해도 프로퍼티의 강한 참조로 인해 reference count는 0이 되지 않고 1인 상태이다.

이를 해결하기 위한 2가지 방법이 있다. 바로 Weak(약한 참조)Unowned(소유 되지 않은 참조)이다.

2 - 2 Weak(약한 참조)

class Person {
    let name: String
    init(name: String) { self.name = name }
    var apartment: Apartment?
    deinit { print("\(name) is being deinitialized") }
}

class Apartment {
    let unit: String
    init(unit: String) { self.unit = unit }
    
    //이부분 Weak❗️
    weak var tenant: Person?
    deinit { print("Apartment \(unit) is being deinitialized") }
}

위에서의 코드와는 Apartment클래스의 프로퍼티 tenant 앞에 weak를 붙인 것 밖에 차이가 없다. 참조관계는 아래와 같아진다.

한쪽은 강하게 한쪽은 약하게 참조하고 있는 상태에서 john에 nil을 할당하는 순간

john = nil
// Prints "John Appleseed is being deinitialized"

다음과 같이 unit4A만 강하게 참조하고 나머지 참조관계는 없어지게 된다. 여기서 중요한 것은 강한 참조 일때와 달리 약한 참조를 한 객체는 ARC에서 자동으로 객체의 메모리까지 해제를 시켜준다는 점이다. 즉 원래라면 unit4A!.tenant = nil을 통해 완전히 해제해야 하지만 weak 키워드가 붙어서 자동으로 해제되었다는 것이다.👍🏻

2 - 3 Unowned(미소유 참조)

메모리 누수를 막을 수 있는 또 다른 방법은 Unowned(소유 되지 않은 참조)이다. 그러나 Weak와 다른 점은 다른 인스턴스와 수명이 같거나 더 긴 경우에 사용된다. 또한 약한 참조와 달리 소유되지 않은 참조에는 항상 값이 있어야 한다. 즉 Optional을 사용할 수 없다는 것이다. 앞에서는 nil을 할당하여 ARC로부터 메모리를 해제 당할 수 있었지만 미소유 참조에서는 그 참조값이 계속 유지된다는 것이다.

사용법은 Weak와 같이 Unowned키워드를 붙여주기만 하면된다.


Strong, Weak, Unowned에 대해서 알아보았다. 정리해보면 상황에 맞게 이 키워드들을 사용해야 하는것이다. 아직 엄청 큰 프로젝트를 안해봤지만 분명 큰 프로젝트를 하게 되고 이런 내용을 모르면 메모리의 심각한 누수를 겪게 될 것이기에 더 공부해야겠다...📚

profile
🧑🏻‍💻iOS

0개의 댓글