자료구조 / 메모리 구조 및 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)을 활용하여 메모리가 정상적으로 해제되었는지 확인가능.
'iOS' 카테고리의 다른 글
[iOS] 2025.02.25 비동기 프로그래밍 / 제네릭 (0) | 2025.02.25 |
---|---|
[iOS] 2025.02.25 클로저 / 객체지향 프로그래밍 (0) | 2025.02.25 |
[iOS] 2025.02.21 Struct와 Class / 프로토콜 (0) | 2025.02.21 |
[iOS] 2025.02.12 프로젝트 : "간단한 가위바위보 게임 만들기" (0) | 2025.02.12 |
[iOS] 2025.02.11 함수의 사용 방법 이해하기 (0) | 2025.02.11 |