IMCAT.DEV
로그인 가입
Swift 중급 자료구조

Swift Swift의 메모리 관리

Swift Swift의 메모리 관리 — 중급

Swift의 메모리 관리

1. 도입 (왜 배워야 하는가)

Swift에서 메모리 관리는 애플리케이션의 성능과 안정성에 직결되는 중요한 요소입니다. Swift는 자동 참조 카운팅(ARC, Automatic Reference Counting)을 사용하여 메모리를 관리하지만, 개발자가 객체의 생명주기와 참조 관계를 이해하지 못하면 메모리 누수나 강한 참조 순환(Strong Reference Cycle)과 같은 문제가 발생할 수 있습니다. 이러한 문제는 앱이 비정상적으로 종료되거나 성능이 저하되는 원인이 됩니다. 따라서, 메모리 관리에 대한 이해는 실무에서 효율적이고 안정적인 코드를 작성하는 데 필수적입니다.

2. 핵심 개념 설명

2.1 자동 참조 카운팅 (ARC)

ARC는 Swift의 메모리 관리 방법으로, 객체가 참조될 때마다 참조 카운트를 증가시키고, 더 이상 참조되지 않을 때 카운트를 감소시킵니다. 카운트가 0이 되면 해당 객체는 메모리에서 해제됩니다.

비유

ARC를 집의 관리자로 비유할 수 있습니다. 집에 누군가가 들어오면 관리자는 인원 수를 세고, 누군가 나가면 인원 수를 줄입니다. 집에 아무도 남지 않으면 관리자는 집을 청소합니다.

2.2 참조 타입과 값 타입

  • 참조 타입: 클래스, 함수, 클로저. 객체의 주소를 참조합니다.
  • 값 타입: 구조체, 열거형. 값 자체를 복사하여 전달합니다.
  • 타입예시메모리 관리 방식
    참조 타입클래스, 클로저ARC를 사용하여 관리
    값 타입구조체, 열거형스택에 저장되며 자동 해제

    2.3 강한 참조 순환

    두 객체가 서로를 강하게 참조하는 경우 발생하는 문제입니다. 이로 인해 두 객체 모두 해제되지 않고 메모리에 남아 있게 됩니다.

    3. 기본 코드 예제

    class Person {
        var name: String
        init(name: String) {
            self.name = name
        }
    }
    
    class Apartment {
        var tenant: Person?
    }
    
    var john: Person? = Person(name: "John")
    var apartment: Apartment? = Apartment()
    
    apartment?.tenant = john  // John이 아파트에 거주
    john = nil                // John을 nil로 설정
    
    // 실행 결과: apartment?.tenant는 여전히 John을 참조하고 있습니다.
  • Person 클래스와 Apartment 클래스를 정의합니다.
  • john 변수를 통해 Person 객체를 생성하고, apartment 변수에 Apartment 객체를 저장합니다.
  • apartment?.tenant = john을 통해 Apartment의 임차인으로 john을 설정합니다.
  • john을 nil로 설정해도 apartment는 여전히 john을 참조하고 있습니다. 이로 인해 메모리 누수가 발생할 수 있습니다.
  • 4. 실전 활용 예제

    4.1 강한 참조 순환 해결

    쇼핑몰에서 장바구니를 구현할 때, 상품 객체와 장바구니 객체가 서로를 참조한다고 가정해보겠습니다. 이때 강한 참조 순환이 발생하지 않도록 weak 참조를 사용합니다.

    class Product {
        var name: String
        init(name: String) {
            self.name = name
        }
    }
    
    class Cart {
        var items: [Product] = []
    }
    
    var cart: Cart? = Cart()
    var product: Product? = Product(name: "Laptop")
    
    cart?.items.append(product!) // 제품을 장바구니에 추가
    product = nil                // 이제 product는 nil로 설정
  • Product와 Cart 클래스를 정의하고, Product 객체를 장바구니에 추가한 후 product를 nil로 설정합니다. 이 경우, 강한 참조 순환이 발생하지 않도록 주의해야 합니다.
  • 4.2 클로저와 메모리 관리

    클로저에서 self를 강하게 참조할 경우, 순환 참조가 발생할 수 있습니다. 이를 방지하기 위해 weak 또는 unowned 참조를 사용합니다.

    class MyClass {
        var name: String
        init(name: String) {
            self.name = name
        }
        
        lazy var printName: () -> Void = { [weak self] in
            guard let self = self else { return }
            print("My name is \(self.name)")
        }
    }
    
    let myObject = MyClass(name: "Swift")
    myObject.printName() // My name is Swift
  • MyClass에서 클로저를 정의할 때 self를 weak 참조하여 순환 참조를 방지합니다.
  • 5. 흔한 실수와 주의사항

    실수 1: 강한 참조 순환

    class A {
        var b: B?
    }
    
    class B {
        var a: A?
    }
    
    // 잘못된 사용: A와 B가 서로를 강하게 참조
    var a = A()
    var b = B()
    a.b = b
    b.a = a // 이로 인해 순환 참조가 발생

    올바른 사용:

    class A {
        weak var b: B? // 약한 참조로 변경
    }
    
    class B {
        var a: A?
    }
  • A 클래스의 b 속성을 weak로 변경하여 강한 참조 순환을 방지합니다.
  • 실수 2: 불필요한 강한 참조

    class MyClass {
        var name: String
        init(name: String) {
            self.name = name
        }
    }
    
    var myObject: MyClass? = MyClass(name: "Test")
    myObject = nil // 메모리 해제
  • 이 경우에는 강한 참조가 필요하지 않으므로, 불필요한 참조를 피해야 합니다.
  • 6. 정리

  • Swift의 메모리 관리는 ARC를 통해 자동으로 관리되지만, 개발자는 객체의 생명주기를 이해해야 합니다.
  • 참조 타입과 값 타입의 차이를 이해하고, 강한 참조 순환을 피하기 위해 weak 또는 unowned 참조를 사용해야 합니다.
  • 클로저를 사용할 때 self를 weak로 참조하여 메모리 문제가 발생하지 않도록 주의해야 합니다.

다음 학습 주제로는 Swift의 프로토콜과 델리게이트 패턴을 추천합니다. 이는 메모리 관리와 함께 애플리케이션의 구조를 개선하는 데 도움이 됩니다.