카테고리 없음

[iOS / Swift] iOS 메모리 구조와 앱 메모리

이도서 2025. 3. 9. 14:16

1. iOS 메모리 구조

iOS에서 앱이 실행되면 메모리는 4가지 영역으로 나뉨

메모리 영역 하는 일 예시
Stack (스택) 지역 변수 저장(자동 관리됨) 함수 안에서 만든 변수 (`let`, `var`)
Heap (힙) 동적으로 할당되는 데이터 저장 `class`인스턴스, `UIImage`등
Date (데이터) 전역 변수, static변수 저장 `static var count = 0`
Code (코드) 실행할 코드 저장 컴파일된 앱 코드

 

예제

class Person {
    var name: String  // Heap 영역에 저장됨 (동적 할당됨)

    init(name: String) {
        self.name = name
    }
}

func sayHello() {
    let greeting = "Hello"  // Stack 영역에 저장됨 (함수 끝나면 사라짐)
    let user = Person(name: "John") // Heap에 저장됨 (직접 해제 필요)
    
    print("\(greeting), \(user.name)!")
}
  • `greeting` 변수는 Stack에 저장됨 => 함수 종료 시 자동 해제
    • `greeting`은 String 타입이지만, Swift에서 `String`은 값 타입이기 때문에 Stack에 저장
    • 함수 `sayHello()`가 끝나면 자동으로 사라짐
  • `Person` 인스턴스는 Heap에 저장됨 => 직접 해제해줘야 함(ARC 사용)
    • `Person`은 class(참조 타입)이기 때문에, 객체가 Heap에 저장
    • `user` 변수는 Stack에 저장되지만, Heap에 있는 `Person` 객체를 가리키는 포인터(주소)만 저장
    • 함수 `sayHello()`가 끝나도 ARC에 의해 해제될 때까지 메모리에 남아 있음

 

그러면 메모리를 어느정도 관리해주니까 조금 덜 해도 되는거 아닐까? 하는 생각이 들었음.


2. iOS의 메모리 관리 방법

iOS는 자동으로 메모리를 관리하지만, 몇 가지 개념을 알아야 함.

 

2-1 ARC (Automatic Reference Counting)

iOS에서는 ARC(자동 참조 카운트)를 사용해서 객체가 필요 없으면 자동으로 메모리를 해제해줌.

쉽게 말하면, 객체를 몇개의 변수가 참조하고 있는지 체크해서 아무도 안 쓰면 삭제하는 방식임.

class Animal {
    var name: String
    
    init(name: String) {
        self.name = name
        print("\(name) 생성됨!")  // 객체 생성 시 메시지 출력
    }
    
    deinit {
        print("\(name) 해제됨!")  // 객체 해제될 때 메시지 출력
    }
}

func createAnimal() {
    let dog = Animal(name: "Buddy") // ARC 카운트: 1
} // 함수 종료 시 dog 참조가 사라지므로 ARC 카운트 0 -> 메모리 해제됨

// 출력 결과
Buddy 생성됨! 
Buddy 해제됨!

=> 함수가 끝나면 `dog`를 참조하는 곳이 없기 때문에, 자동으로 메모리에서 삭제됨.

 

그런데 이정도면 메모리를 어느정도 관리해준다는 소리 같은데, 메모리가 부족해지기가 쉽지 않을 것 같은데..?


3. Memory Warning이 발생하는 이유

앱이 메모리를 너무 많이 사용하면 Memory Warning이 발생

iOS는 메모리가 부족하면 앱을 강제로 종료할 수도 있음

 

Memory Warning 발생 원인

  1. 이미지를 한꺼번에 너무 많이 로드할 때
  2. 불필요한 객체를 계속 만들고 지우지 않을 때(메모리 누수 = Memory leak)
  3. 백그라운드에서 앱이 너무 많이 실행 중일 때

Memory Warning 대처 방법

iOS는 `didReceiveMemoryWarning()`을 호출해서 앱이 불필요한 메모리를 정리하도록 요청함

이때, 캐시 데이터를 삭제하거나 사용하지 않는 객체를 해제해야 함

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    imageCache.removeAllObjects() // 이미지 캐시 삭제 (메모리 절약)
}

이렇게 하면 앱 내에서 임시 저장한 이미지 데이터를 삭제해서 메모리를 확보 가능, 또는

`UIImage(contentsOfFile:)`을 사용해서 필요할 떄만 이미지를 로드하는 것도 좋은 방법임


4. 메모리 누수 방지 방법(Strong Reference Cycle 해결하는 방법)

ARC가 자동으로 메모리를 관리하지만, 순환 참조가 생기면 메모리 해제가 안됨

그 말인 즉슨, 객체들이 서로 강하게 참조하면 ARC가 해제하지 못해서 메모리 누수가 발생한다는 뜻임.

 

메모리 누수가 발생하는 코드를 보겠음

class A {
    var b: B?  // 강한 참조
    
    init() { print("A 생성됨") }
    deinit { print("A 해제됨") }
}

class B {
    var a: A?  // 강한 참조
    
    init() { print("B 생성됨") }
    deinit { print("B 해제됨") }
}

var objA: A? = A()
var objB: B? = B()

objA?.b = objB
objB?.a = objA

objA = nil
objB = nil // 메모리 해제 안 됨! (순환 참조 발생)

위 코드에서 

class A {
    weak var b: B?  // 약한 참조 (ARC가 해제 가능!)
    
    init() { print("A 생성됨") }
    deinit { print("A 해제됨") }
}

class B {
    var a: A?
    
    init() { print("B 생성됨") }
    deinit { print("B 해제됨") }
}

이렇게 `weak` 혹은 `unowned` 를 사용하면 됨

`weak`을 사용하면 ARC가 자동으로 메모리를 해제할 수 있음

즉, `weak`, `unowned`는 순환참조가 발생할 수 있는 경우 사용하면 좋음

  weak unowned
ARC참조 카운트 증가 여부 증가 안함 증가 안함
nil 허용 여부 가능 (Optional) 불가능 (Non-Optional)
객체가 해제되면? 자동으로 nil로 변경됨 해제된 메모리를 참조하면 Crash 발생
언제 사용함? 객체가 nil이 될 수 있는 경우 객체가 nil이 될 수 없는 경우

공부는 하는데 기초지식이 없으니 메모리 겁나 어렵네.. 더 파봐야겠음.