[swift] 자원 해제 에러

스위프트(Swift)를 사용하다 보면 자원 해제 에러가 발생할 수 있습니다. 이러한 에러는 다양한 이유로 발생할 수 있지만 주로 다음과 같은 경우에 발생합니다:

1. 댕글링 포인터(Dangling Pointer)

댕글링 포인터는 이미 해제된 메모리를 가리키는 포인터를 의미합니다. 이는 메모리 누수와 관련된 문제로, 예기치 않은 동작을 유발할 수 있습니다.

자원 해제 에러를 방지하기 위해서는 댕글링 포인터가 발생하지 않도록 주의해야 합니다. 예를 들어, 객체를 참조하는 포인터가 있는데 해당 객체가 해제되었다면 포인터를 nil로 설정하거나 유효하지 않은 값으로 초기화해야 합니다.

var myObject: MyClass? = MyClass()
// myObject 사용

// 자원을 해제하고 myObject를 nil로 설정
myObject = nil

2. 약한 참조 오류(Weak Reference Error)

약한 참조는 참조 카운트를 증가시키지 않는 참조입니다. 그렇기 때문에 해당 객체가 해제되면 약한 참조는 자동으로 nil로 설정됩니다. 하지만 약한 참조로 인한 자원 해제 에러가 발생할 수도 있습니다.

약한 참조는 주로 클로저와 관련이 있습니다. 예를 들어, 클로저 내부에서 약한 참조로 객체에 접근하고 있는데 해당 객체가 이미 해제되었다면 자원 해제 에러가 발생할 수 있습니다. 이러한 경우 클로저 내부에서 optional chaining을 사용하여 예외 처리를 해주어야 합니다.

class MyClass {
    var closure: (() -> Void)?
    
    deinit {
        print("MyClass가 해제되었습니다.")
    }
}

var myObject: MyClass? = MyClass()
myObject?.closure = { [weak myObject] in
    guard let strongSelf = myObject else {
        return
    }
    
    // strongSelf 사용
}

// myObject를 해제하면 MyClass 인스턴스도 함께 해제됩니다.
myObject = nil

3. 레퍼런스 카운트 오류(Reference Count Error)

레퍼런스 카운트 오류는 개발자가 메모리 관리에 실수를 한 경우에 발생합니다. 예를 들어, 참조 카운트를 증가시키지 않거나 감소시키지 않은 경우, 객체에 대한 강한 참조 순환(Strong Reference Cycle)이 있는 경우 등이 있습니다. 이러한 오류는 ARC(Automatic Reference Counting)가 객체를 해제할 때 감지하고 자원을 해제하며, 런타임 에러가 발생할 수 있습니다.

레퍼런스 카운트 오류를 방지하기 위해서는 명시적으로 메모리 관리를 해주어야 합니다. 강한 참조 순환을 방지하기 위해 약한 참조(w eak)이나 비소유 참조(unowned)를 사용하고, 자원을 해제해야 할 때는 적절한 시점에 deinit 메소드를 활용해 자원을 해제해야 합니다.

class MyClass {
    weak var otherObject: OtherClass?
    
    deinit {
        print("MyClass가 해제되었습니다.")
    }
}

class OtherClass {
    unowned let myObject: MyClass
    
    init(myObject: MyClass) {
        self.myObject = myObject
    }
    
    deinit {
        print("OtherClass가 해제되었습니다.")
    }
}

var myObject: MyClass? = MyClass()
var otherObject: OtherClass? = OtherClass(myObject: myObject!)

myObject?.otherObject = otherObject
otherObject = nil

// myObject를 해제하면 MyClass 인스턴스도 함께 해제됩니다.
myObject = nil

참고 문서