3월 12일 TIL (ARC와 메모리 누수)

이승원·2024년 3월 12일
0

TIL

목록 보기
38/75
post-thumbnail

ARC (Automatic Reference Counting)

  • ARC 는 메모리 관리 기법 중 하나로, 객체나 인스턴스가 참조되는 횟수를 추적하여 메모리에서 해제할 시점을 결정합니다.
  • ARC의 작동방식 :
    • 객체 생성: 객체가 생성되면 참조 횟수가 1 증가합니다.
    • 객체 참조: 객체를 다른 변수나 상수에 할당하면 해당 객체의 참조 횟수가 1 증가합니다.
    • 참조 해제: 객체의 참조가 없어지면 (참조하는 변수나 상수가 없가나 nil 이 할당되면) 참조 횟수가 1 감소합니다.
    • Zeroing Weak Reference: 약한 참조 (Week Reference)는 객체의 참조 횟수를 증가시키지 않고 추적합니다. 객체가 해제되면 약한 참조는 자동으로 nil로 설정됩니다.
class Person {
    let name: String
    init(name: String) {
        self.name = name
        print("\(name) is being initialized")
    }
    deinit {
        print("\(name) is being deinitialized")
    }
}

var reference1: Person?
var reference2: Person?
var reference3: Person?

reference1 = Person(name: "John Appleseed") // RC: 1️⃣
// Prints "John Appleseed is being initialized"

reference2 = reference1 // RC: 2️⃣
reference3 = reference1 // RC: 3️⃣

reference1 = nil // RC: 2️⃣
reference2 = nil // RC: 1️⃣

reference3 = nil // RC: 0️⃣
// Prints "John Appleseed is being deinitialized"

강한 참조 순환 (Strong Reference Cycle)

  • 우리가 흔히 사용하는 참조는 디폴트로 강한 참조 (Strong Reference)를 사용한다. 이 강한 참조를 잘못 사용하면 메모리 누수 (Memory Leak)가 발생할 수 있다.
  • 가장 대표적인 예시가 강한참조순환이다. 두 개 이상의 인스턴스가 서로를 참조해서 발행하는 문제다.
class Man {
    var name : String
    var girlFriend : Woman?
    
    init(name:String){
        self.name = name
    }
    deinit {print(" Man Deinit ")}
}

class Woman {
    var name : String
    var boyFriend : Man?
    
    init(name:String){
        self.name = name
    }
    deinit {print(" Woman Deinit ")}
}

var sam : Man? = Man(name: "Sam") // Reference Counter : 1
var julia : Woman? = Woman(name: "Julia") // Reference Counter : 1

sam?.girlFriend = julia  // Reference counter : 2
julia?.boyFriend = sam  // Reference counter : 2
                  
sam = nil // Reference counter : 1
julia = nil // Reference counter : 1


//둘다 Reference Counter 가 0 이 아니기 때문에, deinit가 호출되지 않음, 즉 메모리에 아직 남아 있는 상태.

강한 참조 순환 문제 해결 방법

약한 참조 (Weak Reference)

  • 약한 참조 (Weak Reference): 참조되는 대상을 약하게 참조하여 순환참조를 방지한다.
    • weak는 옵셔널로 선언되는 참조이다.
    • 약한 참조는 참조하는 객체를 강제로 유지하지 않고, 참조 대상이 메모리에서 해제되면 자동으로 nil로 설정된다.
    • 순한 참조를 방지하기 위해 사용되고 한쪽을 weak로 선언하여 순환 참조 문제를 해결할 수 있다. 근데 양쪽에 weak로 선언하는게 일반적이라고 한다.
class Man {
    var name : String
    weak var girlFriend : Woman?
    
    init(name:String){
        self.name = name
    }
    deinit {print(" Man Deinit ")}
}

class Woman {
    var name : String
    var boyFriend : Man?
    
    init(name:String){
        self.name = name
    }
    deinit {print(" Woman Deinit ")}
}

var sam : Man? = Man(name: "Sam") // Reference Counter : 1
var julia : Woman? = Woman(name: "Julia") // Reference Counter : 1

sam?.girlFriend = julia  // Reference counter : 1
julia?.boyFriend = sam  // Reference counter : 1
                  
sam = nil // Reference counter : 0
julia = nil // Reference counter : 0

비소유 참조 ( Unowned Reference)

  • 비소유 참조 ( Unowned Reference) : 참조되는 대상이 항상 유효한 경우에만 사용하며, 해당 대상이 해제될 수 있는 상황에는 사용하지 않는다.
    • unowned는 옵셔널이 아닌 비소유 참조를 나타낸다.
    • 비소유 참조는 항상 값이 있다고 가정하며, 참조하는 객체가 해제되면 런타임 에러가 발생할 수 있다.
class Man {
    var name : String
    unowned var girlFriend : Woman?
    
    init(name:String){
        self.name = name
    }
    deinit {print(" Man Deinit ")}
}

class Woman {
    var name : String
    unowned var boyFriend : Man?
    
    init(name:String){
        self.name = name
    }
    deinit {print(" Woman Deinit ")}
}

var sam : Man? = Man(name: "Sam") // Reference Counter : 1
var julia : Woman? = Woman(name: "Julia") // Reference Counter : 1

sam?.girlFriend = julia  // Reference counter : 1
julia?.boyFriend = sam  // Reference counter : 1
                  
sam = nil // Reference counter : 0
julia = nil // Reference counter : 0

sam.girlfriend // 오류 
profile
개발자 (진)

0개의 댓글