배열, 큐, 스택 + ARC와 순환 참조 완전 정리

Ios_Roy·2025년 7월 29일
0

TIL

목록 보기
13/25
post-thumbnail

Swift 개발자를 위한 기본기 다지기

배열, 큐, 스택 + ARC와 순환 참조 완전 정리

앱 개발을 하다 보면 자료구조나 메모리 구조에 대한 이해가 코드 안정성과 성능에 직결됩니다.
오늘은 Swift에서 자주 쓰이는 배열, 큐, 스택과 함께, 메모리 관리의 핵심인 ARC와 순환 참조 문제까지 정리해보겠습니다.


07.29(화)

제가 이해하고 공부 한대로 작성한 내용이다 보니 , 정확한 정보가 아닐수도 있습니다
고쳐야 할 부분이 있다면 , 편하게 애기 해주시면 감사 하겠습니다🙏🏻


1️⃣ 자료구조 바로 알기

📦 배열 (Array)

  • 데이터를 순차적으로 저장하는 구조입니다.
  • 인덱스를 이용해 O(1) 시간에 접근이 가능하나, 삽입/삭제는 느릴 수 있음(O(n)).
var numbers = [1, 2, 3, 4, 5]
numbers.append(6)
print(numbers) // [1, 2, 3, 4, 5, 6]

📌 사용 예시

  • 게시글 목록
  • 설정 항목 리스트 등

🚃 큐 (Queue)

  • 선입선출(FIFO, First-In First-Out) 구조입니다.
  • 한쪽 끝에서 데이터를 삽입하고, 반대쪽 끝에서 제거합니다.
struct Queue<T> {
    private var elements: [T] = []

    mutating func enqueue(_ element: T) {
        elements.append(element)
    }

    mutating func dequeue() -> T? {
        return elements.isEmpty ? nil : elements.removeFirst()
    }
}

📌 사용 예시

  • 프린터 작업 대기열
  • BFS 탐색
  • 네트워크 요청 처리 등

🧱 스택 (Stack)

  • 후입선출(LIFO, Last-In First-Out) 구조입니다.
  • 데이터는 한쪽 끝(Top)에서만 삽입(push)과 제거(pop)이 일어납니다.
struct Stack<T> {
    private var elements: [T] = []

    mutating func push(_ element: T) {
        elements.append(element)
    }

    mutating func pop() -> T? {
        return elements.popLast()
    }

    func peek() -> T? {
        return elements.last
    }
}

📌 사용 예시

  • 함수 호출 스택
  • 괄호 짝 검사
  • Undo/Redo 기능 등

🧠 2. 메모리 구조 및 ARC

📚 메모리 구조 요약

구역설명
Stack함수 호출, 지역 변수 저장
Heap클래스 인스턴스 등 동적 메모리 저장
Code실행 코드 저장 영역
Data전역 변수, 정적 변수 저장

🔁 ARC (Automatic Reference Counting)

  • Swift의 메모리 관리 방식입니다.
  • 클래스 인스턴스의 참조 횟수(reference count)를 자동으로 추적하여, 더 이상 사용되지 않으면 메모리에서 해제합니다.

❗️문제: 순환 참조 (Circular Reference)

두 개 이상의 객체가 서로를 강하게 참조(strong)하면, 서로 해제가 안 되는 문제가 발생합니다.


❌ 순환 참조 예시

class Person {
    var name: String
    var pet: Pet?

    init(name: String) {
        self.name = name
        print("👤 \(name) 초기화됨")
    }

    deinit {
        print("👤 \(name) 해제됨")
    }
}

class Pet {
    var owner: Person?

    init() {
        print("🐶 Pet 초기화됨")
    }

    deinit {
        print("🐶 Pet 해제됨")
    }
}

do {
    let person = Person(name: "Alice")
    let pet = Pet()
    
    person.pet = pet
    pet.owner = person  // 🔁 서로 강한 참조 → 순환 참조 발생
}

출력 결과

deinit이 호출되지 않음 → 메모리 누수 발생


✅ 해결 방법: weak 키워드 사용

weak 참조는 ARC의 참조 카운트에 영향을 주지 않으며, 자동으로 nil 처리됩니다.

class Pet {
    weak var owner: Person?  // 🔑 약한 참조로 순환 참조 해결
}

✅ 순환 참조 해결 예시 (완성 코드)

class Person {
    var name: String
    var pet: Pet?

    init(name: String) {
        self.name = name
        print("👤 \(name) 초기화됨")
    }

    deinit {
        print("👤 \(name) 해제됨")
    }
}

class Pet {
    weak var owner: Person?

    init() {
        print("🐶 Pet 초기화됨")
    }

    deinit {
        print("🐶 Pet 해제됨")
    }
}

do {
    let person = Person(name: "Alice")
    let pet = Pet()

    person.pet = pet
    pet.owner = person
}

출력 결과

👤 Alice 초기화됨
🐶 Pet 초기화됨
👤 Alice 해제됨
🐶 Pet 해제됨


✔️ deinit이 정상적으로 호출되어 메모리 누수가 해결됨

✅ 마무리

  • 자료구조는 기본이지만, Swift에서는 제네릭 + 구조체로 깔끔하게 구현할 수 있습니다.
  • 메모리 관리에서는 ARC와 순환 참조 개념이 중요하며, 실수 하나로 메모리 누수가 생길 수 있습니다.
  • weak, unowned는 상황에 따라 적절히 선택하여 안정적인 코드 구조를 만드세요.

🧑‍💻 이 글이 Swift 개발 입문자 또는 중급자에게 기초를 다시 정리하는 데 도움이 되었기를 바랍니다.
궁금한 점이나 더 다뤄줬으면 하는 주제가 있다면 댓글로 남겨주세요!

profile
iOS 개발자 공부하는 Roy

0개의 댓글