값 타입(Value Type) vs 참조 타입(Reference Type)
Swift에서는 모든 데이터가 값 타입 또는 참조 타입으로 분류.
이 두 타입은 메모리에서 데이터를 저장하고 관리하는 방식이 다르며, 각각의 타입은 데이터의 복사 및 공유 방식에 큰 차이가 존재.
1. 값 타입(Value Type)
값 타입은 데이터를 복사하여 저장. 변수나 상수에 값을 할당하거나, 함수에 값을 전달할 때 새로운 복사본 생성.
따라서 값 타입을 사용하는 경우, 각각의 인스턴스는 독립적인 값을 가지며, 다른 변수에 영향을 주지 않음.
- 대표적인 값 타입: struct, enum, tuple
1.1. 특징
- 복사
- 값을 할당하거나 전달할 때 값이 복사.
- 독립성
- 하나의 값 타입 인스턴스가 변경되어도 다른 인스턴스에 영향을 미치지 않음.
- 성능
- 스택(stack)에 저장되어 빠른 메모리 접근이 가능.
- 함수가 종료되면 스택에서 메모리가 자동으로 해제
1.2. 예시
struct Point {
var x: Int
var y: Int
}
var point1 = Point(x: 10, y: 20)
var point2 = point1 // point1의 값을 point2에 복사
point2.x = 30
print(point1.x) // 10 (point1은 영향을 받지 않음)
print(point2.x) // 30
- point1의 값이 point2로 복사되었으며, point2의 값을 변경해도 point1에 영향을 주지 않음.
1.3. 사용하는 이유
- 값 타입은 데이터를 복사하므로, 객체의 변경으로 인한 부작용을 방지할 수 있음.
- 불변성을 유지하며 데이터를 처리할 때 값 타입이 유용.
- 스레드 안전성을 보장해야 하는 경우 적합.
- Swift 표준 라이브러리의 기본 타입 (Int, Double, Bool, String 등)은 모두 값 타입.
2. 참조 타입(Reference Type)
참조 타입은 데이터를 참조로 저장하며, 여러 변수가 동일한 인스턴스를 참조할 수 있음.
참조 타입에서는 데이터를 복사하지 않고, 참조하는 메모리 주소를 공유.
따라서 하나의 참조 타입 인스턴스를 여러 곳에서 공유할 경우, 한 곳에서 인스턴스를 변경하면 모든 참조 변수에 영향을 미치게 됨.
- 대표적인 참조 타입: class, closure
2.1. 특징
- 참조
- 값을 할당하거나 전달할 때 값이 복사되지 않고 참조만 전달.
- 공유
- 여러 변수가 동일한 인스턴스를 참조하므로, 한 곳에서 변경하면 다른 모든 참조에 영향.
- 메모리
- 힙(heap)에 저장되며, ARC(Automatic Reference Counting)가 메모리 관리.
- 참조 카운트가 0이 되면 메모리가 해제.
2.2. 예시
class Person {
var name: String
init(name: String) {
self.name = name
}
}
var person1 = Person(name: "John")
var person2 = person1 // person1과 동일한 인스턴스를 참조
person2.name = "Alice"
print(person1.name) // Alice (person1도 영향을 받음)
print(person2.name) // Alice
- person1과 person2는 동일한 Person 인스턴스를 참조하므로, 한 쪽을 변경하면 다른 쪽도 변경.
2.3. 사용하는 이유
- 참조 타입은 상태를 공유해야 하는 경우 적합.
- 예를 들어, 여러 객체가 같은 데이터나 상태를 공유하며 협력해야 할 때.
- 클래스(class)는 상속을 지원하여 객체 지향 프로그래밍에서 중요한 역할.
- 참조 타입은 상속, 다형성 등의 객체 지향 프로그래밍 패턴을 구현할 수 있어 복잡한 구조를 구성할 때 유용.
3. 값 타입과 참조 타입의 비교
값 타입 (Value Type) | 참조 타입 (Reference Type) | |
메모리 위치 | 스택(Stack) | 힙(Heap) |
복사 여부 | 값이 복사됨 (독립적인 값) | 참조가 복사됨 (메모리 주소를 공유) |
변경 영향 | 값이 변경되더라도 다른 변수에 영향 없음 | 하나의 변수를 변경하면 모든 참조 변수에 영향을 줌 |
사용되는 타입 | struct, enum, tuple | class, closure |
메모리 해제 | 함수가 종료되면 자동으로 스택에서 해제 | ARC가 참조 카운트에 따라 메모리 해제 |
주요 사용 사례 | 불변성 유지가 필요한 경우, 독립적인 값 필요 시 | 상속, 다형성 등 객체 지향 패턴이 필요한 경우 |
4. 선택 가이드
4.1. 값 타입을 선택하는 경우
- 독립적인 데이터
- 복사된 데이터가 서로 독립적이어야 하는 경우 값 타입을 사용.
- 좌표값이나 사용자 정보처럼 서로 영향을 주지 않도록 하는 것.
- 불변성
- 불변성(immutable)을 유지해야 할 때 값 타입을 사용하면 데이터의 안전성을 높일 수 있음.
- 경량 객체
- 값 타입은 스택에 저장되므로 메모리 관리 오버헤드가 적고, 경량 객체에서 성능상의 이점 존재.
4.2. 참조 타입을 선택하는 경우
- 상태 공유
- 여러 객체가 동일한 데이터를 공유해야 할 때 참조 타입이 적합.
- 뷰 컨트롤러 간에 데이터 모델을 공유하거나 앱의 글로벌 설정 값을 공유할 때.
- 객체 지향 패턴
- 상속이나 다형성 등 객체 지향 프로그래밍의 개념을 구현할 때 참조 타입이 필요하며, 특히 클래스 상속을 통해서 코드의 재사용성을 높일 수 있음.
5. 스위프트에서 값 타입과 참조 타입의 결합
Swift는 값 타입을 기본으로 하지만, 참조 타입과 결합하여 강력한 프로그래밍 패턴을 만들 수 있음.
예를 들어, 값 타입인 구조체와 참조 타입인 클래스 또는 클로저를 결합하여 효율적이고 안전한 코드 작성 가능.
struct Task {
var title: String
var owner: Person // Person은 참조 타입
}
class Person {
var name: String
init(name: String) {
self.name = name
}
}
let john = Person(name: "John")
var task = Task(title: "Learn Swift", owner: john)
task.owner.name = "Alice"
print(john.name) // Alice (참조 타입이므로 변경된 이름이 반영됨)
- 값 타입인 Task는 독립적인 값으로 복사되지만, 참조 타입인 Person을 소유하고 있으므로 내부의 참조된 객체는 공유 됨.
6. 메모리의 4가지 영역
- 코드 영역 (Text Segment)
- 실행할 프로그램의 코드가 저장.
- 함수, 메소드와 같이 실행 가능한 명령어들이 위치.
- 데이터 영역 (Data Segment)
- 전역 변수, 정적 변수, 상수가 저장.
- 프로그램 시작부터 종료까지 메모리에 고정되며, 주로 프로그램 실행 동안 유지되는 전역 데이터가 이 곳에 저장 됨.
- 힙 영역 (Heap)
- 동적 메모리 할당이 이루어지는 공간.
- 런타임에 필요할 때마다 메모리를 할당하고, 더 이상 필요하지 않으면 해제 됨.
- 참조 타입인 클래스의 인스턴스와 같은 객체들이 주로 이 곳에 저장 됨.
- 스택 영역 (Stack)
- 함수 호출 시 지역 변수나 함수의 매개변수가 저장되는 공간.
- 값 타입이 저장되며, LIFO(Last In, First Out) 방식으로 메모리가 관리됨.
- 함수가 종료되면 스택에서 자동으로 메모리가 해제.
7. 메모리 관리 (ARC - Automatic Reference Counting)
참조 타입은 ARC(Automatic Reference Counting)에 의해 메모리 관리.
ARC는 참조 카운트를 추적하여 더 이상 참조되지 않는 객체가 있을 때 힙에서 해당 객체를 해제.
ARC는 자동으로 동작하므로, 프로그래머가 직접 메모리 해제를 관리하지 않아도 됨.
그러나 강한 참조 순환(Strong Reference Cycle)과 같은 문제가 발생할 수 있으므로, 적절한 메모리 관리 기법이 필요.
ARC의 동작 방식
- 강한 참조(Strong Reference)
- 변수가 인스턴스를 참조할 때 참조 카운트가 1 증가.
- 참조 해제
- 변수가 더 이상 인스턴스를 참조하지 않을 때 참조 카운트가 1 감소.
- 메모리 해제
- 참조 카운트가 0이 되면, 해당 객체가 힙 메모리에서 해제.
'Swift' 카테고리의 다른 글
[Swift] inout (0) | 2024.04.08 |
---|---|
[Swift] Escaping Closure (@escaping) (0) | 2024.04.05 |
[Swift] Performance Optimization Tips (0) | 2024.04.04 |
[Swift] 성능 최적화, WMO(Whole Module Optimization) (0) | 2024.04.03 |
[Swift] final 키워드와 Type에 따른 Dispatch (0) | 2024.04.03 |