iOS

[iOS] 2025.02.24 자료구조 / 메모리 구조 및 ARC

ioskkt 2025. 2. 24. 18:26

자료구조 / 메모리 구조 및 ARC

1.  자료구조

배열(Array)

  • 데이터를 순차적으로 저장하는 자료구조
  • 인덱스를 사용해 특정 요소에 접근.
  • 특징 : 빠른 조회, 삽입/삭제 시 성능 저하

 

  • 추가
  • append() -> 마지막에 추가
  • += -> 여러개 추가
  • insert(at:) -> 원하는 위치에 추가

 

  • 삭제
  • remove(at:) -> 특정 위치 요소 삭제
  • removeLast() -> 마지막 요소 삭제
  • removeAll() -> 전체 요소 삭제

 

  • 조회
  • .first -> 첫번째 요소 가져오기
  • .last -> 마지막 요소 가져오기

* second는 안됐음. 옵셔널이므로 !(강제 언래핑) 필요

  • .isEmpty -> 배열이 비어 있는지 확인.

 

  • 수정

var items = ["Book", "Laptop", "Phone"]
items[1] = "Tablet"
print(items) // ["Book", "Tablet", "Phone"]

 

  • 정렬 [ sorted(), sort() ]

var scores = [50, 90, 30, 70, 100]

let sortedScores = scores.sorted() // 새 배열 생성
print(sortedScores) // [30, 50, 70, 90, 100]

scores.sort() // 기존 배열을 정렬
print(scores) // [30, 50, 70, 90, 100]

 

  • 역순 정렬[ reversed() ]

let reversedScores = scores.reversed()
print(reversedScores) // [100, 90, 70, 50, 30]

 

  • 조건 필터링 [ filter() ]

let evenNumbers = scores.filter { $0 % 2 == 0 }
print(evenNumbers) // [30, 50, 90, 100]

 

  • 배열 변환 (map, reduce)

map() -> 모든 요소 변환

let prices = [1000, 2000, 3000]
let discountPrices = prices.map { $0 * 0.9 }

print(discountPrices) // [900.0, 1800.0, 2700.0]

 

reduce() -> 모든 요소 합산

let total = prices.reduce(0) { $0 + $1 }
print(total) // 6000

reduce(초깃값) { 누적값, 현재값 in }

 

  • 배열 포함 여부 확인(contains)

let fruits = ["Apple", "Banana", "Cherry"]

print(fruits.contains("Banana")) // true
print(fruits.contains("Mango")) // false

 

  • 배열 개수 확인(count)

print(fruits.count) // 3

 

  • 배열의 특정 범위 가져오기(prefix, suffix)

let numbers = [10, 20, 30, 40, 50]

print(numbers.prefix(3)) // [10, 20, 30] → 앞에서 3개
print(numbers.suffix(2)) // [40, 50] → 뒤에서 2개

* $ 의 기능이 메서드별로 차이가 있다는 것을 확인.

 

큐(Queue)

  • 선입선출(FIFO, First In First Out) 구조.
  • 데이터를 한쪽에서 삽입하고, 반대쪽에서 제거.
  • 사용 사례 : 프린터 작업 대기열, BFS 탐색

 

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()
    }
}

 

*elements: [T]

Queue에 저장될 요소들을 담는 배열.

T는 제네릭 타입으로, 큐가 어떤 타입의 데이터를 저장할지 동적으로 지정할 수 있음.(어떤 타입이든 받아들일 수 있음.)

Generics란? 타입을 미리 지정하지 않고 코드 내에서 필요에 따라 타입을 지정할 수 있는 기능

 

*enqueue(_ element: T)

큐에 새 요소를 추가하는 함수.

mutating 키워드는 이 함수가 구조체 내의 데이터를 수정하기 때문에 필요.(struct는 기본적으로 값 타입이기 때문에 내부의 값을 변경하려면 mutating을 명시해야함.)

 

*dequeue() -> T?

큐에서 첫 번째 요소를 제거하고 반환하는 함수.

큐가 비어있으면 nil을 반환, 이때 반환 타입은 T? 로 옵셔널로 처리함.

(옵셔널이란? 값이 없거나 있을 수 있음을 표현하는 타입)

elements.removeFirst()는 배열의 첫 번째 요소를 제거하는 함수.

 

스택(Stack)

  • 후입선출(LIFO, Last In First Out) 구조.
  • 데이터를 한쪽에서 삽입하고 제거.
  • 사용 사례 : 함수 호출 스택, 괄호 검사.

 

struct Stack<T> {
    private var elements: [T] = []
    
    mutating func push(_ element: T) {
        elements.append(element)
    }
    
    mutating func pop() -> T? {
        return elements.popLast()
    }
}

 

*push(_ element: T)

스택에 값을 추가하는 함수.

 

*pop() -> T?

pop은 스택에서 값을 제거하고 반환하는 함수. stack의 특성상 마지막에 추가된 항목을 제거함.

popLast() 는 배열에서 마지막 항목을 제거하고 그 값을 반환함. 만약 배열이 비어있으면 nil을 반환함.

 

메모리 구조 및 ARC

1. 메모리 구조

Stack : 함수 호출, 지역 변수 저장.

Heap : 동적 메모리 할당, 참조 타입(Class) 저장.

Code :  실행 중인 코드 저장.

Data : 전역 변수, 정적 변수 저장.

 

2. ARC(Automatic Reference Counting)

  • Swift의 메모리 관리 방식.
  • 클래스 인스턴스의 참조 횟수를 추적하여 필요하지 않은 인스턴스를 메모리에서 해제
  • 순환 참조(Circular Reference) :
  • 두 개 이상의 객체가 서로를 참조하면서 메모리에서 해제되지 않는 상황.
  • 해결 방법 : weak 또는 unowned 키워드 사용.

 

직접 구현해보기

step 1 : 자료구조 구현

1. 큐(Queue) 구현하기 :

  • 정수를 저장하는 Queue 구조체를 구현하세요.
  • enqueue()로 데이터를 삽입하고, dequeue()로 데이터를 제거합니다.
  • 큐의 현재 상태를 출력하는 메서드를 추가하세요.

*변수를 선언해서 큐에 저장된 개수를 확인해보려고

 

var count: Int {

return items.count

}

 

써봤으나 구조체가 제네릭 타입이 아니어서 그런지 오류가 났음. 그래서 정수만 저장하는 타입으로 바꿔봄

제네릭으로도 만들어봄

* Queue<Int> 에서는 var count: Int 를 어떻게 추가하는지 모르겠음. 근데 제네릭 큐를 사용하면 다양한 타입을 처리할 수 있다는걸 알게됨.

 

2. 스택(Stack) 구현하기 :

  • 문자열을 저장하는 Stack 구조체를 구현하세요.
  • push()로 데이터를 삽입하고, pop()으로 데이터를 제거합니다.
  • 스택의 최상단 값을 반환하는 메서드를 추가하세요.

 

*스택에서도 제네릭타입으로 처리 가능. 변수 선언 시 var stringStack = Stack<String>() 처럼 타입을 지정해주면 됨.

 

Step 2 : ARC와 순환 참조 해결

1. 순환 참조 문제 구현

  • 두 클래스를 생성하세요 :
  • Person 클래스 : 이름을 저장하는 속성(name)과 애완동물(pet) 속성을 가짐.
  • Pet 클래스 : 주인(owner) 속성을 가짐.
  • 서로를 참조하는 인스턴스를 생성하고 순환 참조 문제를 확인하세요.

 

2. 순환 참조 해결

  • weak 키워드를 사용해 순환 참조 문제를 해결하세요.
  • 해결 후, 두 인스턴스가 정상적으로 메모리에서 해제되는지 확인하세요.

* person과 pet은 서로 강한 참조를 하고 있어서 nil 로 설정해도 여전히 참조하고 있어 메모리에서 해제 되지 않음.

* weak 키워드를 통해 약한 참조로 바꾸어주면 메모리에서 해제됨.

* 디이니셜라이저(deinit)을 활용하여 메모리가 정상적으로 해제되었는지 확인가능.