일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 자바
- 영화일기
- 영화기록
- Flutter Toy Project
- 도서리뷰
- Flutter
- 토이프로젝트
- 자바 스터디
- 코딩공부
- 인프런
- 영화후기
- IOS
- toy project
- java
- 키노
- 리뷰
- 영화리뷰
- 스프링 입문
- sopt ios
- 플러터
- 자바공부
- 백준
- 일기
- SOPT
- inflearn
- 새벽녘 소소한 기록
- 영화
- backend
- 백엔드
- sopt 35기
- Today
- Total
새벽의 기록
[SOPT] 고량주 1주차 이론 과제 본문
클래스와 구조체에 대해서 딥-다이브 해주세요.
- 값, 주소 타입의 내용이 아닌, 메모리 상에서 작동하는 방식도 이해해봅시다.
- 추가적으로 ARC, 순환 참조에 대해서 함께 알아봅시다.
1. 클래스와 구조체의 차이
https://docs.swift.org/swift-book/documentation/the-swift-programming-language/classesandstructures/
구조체(Struct)와 클래스(Class)는 기본적으로 데이터를 용도에 맞게 묶어 표현하고자 할 때 유용한 기능.
구조체와 클래스는 모두 ‘프로퍼티'와 ‘메서드'를 사용하여 구조화된 데이터와 기능을 가질 수 있고, 하나의 데이터 타입을 만드는 역할을 한다.
클래스 - 참조 타입(Reference type)
- 참조를 전달한다. 변수를 다른 변수에 할당하거나 함수의 인자로 전달할 때, 주소 값(참조)이 복사되면 실제 객체는 공유된다.
- 참조 타입은 메모리 상에서 힙(Heap) 영역에 저장된다. 힙은 동적으로 크기가 변하는 객체들을 관리하는데 적합하며, 할당과 해제가 상대적으로 느리다.
- 클래스는 여러 참조가 같은 객체를 가리킬 수 있어, 한 곳에서 객체를 수정하면 이를 참조하는 모든 곳에 영향을 미친다.
구조체 - 값 타입(Value Type)
- 값을 직접 복사한다. 변수를 다른 변수에 할당하거나 함수의 인자로 전달할 때, 복사된 새로운 값이 전달된다.
- 값 타입은 메모리 상에서 스택(Stack) 영역에 저장된다. 스택은 매우 빠른 메모리 할당과 해제를 지원하며, 일정한 크기의 데이터에 적합하다.
- 각 객체는 독립적인 메모리 공간을 가지므로, 한 객체의 값을 변경해도 다른 객체에 영향을 주지 않는다.
struct MyStruct {
var value: Int
}
class MyClass {
var value: Int
init(value: Int) {
self.value = value
}
}
let structA = MyStruct(value: 5)
var structB = structA // 값 복사
structB.value = 10
print(structA.value) // 5 (원본 유지)
print(structB.value) // 10 (복사본 변경)
let classA = MyClass(value: 5)
let classB = classA // 참조 복사
classB.value = 10
print(classA.value) // 10 (참조된 객체 수정)
print(classB.value) // 10 (같은 객체를 참조)
2. 메모리 상에서의 동작
구조체 - 스택 메모리
- 스택은 LIFO(Last In Firest Out) 형태의 자료구조로 가장 마지막에 들어간 객체가 가장 먼저 나오게 되는 구조.
- 스택은 함수 호출 시에 할당되고, 함수가 끝나면 자동으로 해제되는 구조. Swift의 구조체는 스택에서 고정된 크기의 메모리 공간을 차지하며, 할당과 해제가 빠르다.
- 값 복사 시에도 스택에 새로운 복사본이 생기므로, 두 값은 서로 완전히 독립적.
클래스 - 힙 메모리
- 힙은 객체를 동적으로 할당하는 메모리 공간. 클래스의 인스턴스는 힙에 할당되고, 해당 인스턴스를 가리키는 참조 주소는 스택에 저장된다.
- 여러 변수가 동일한 클래스를 참조할 때, 스택에는 같은 힙 주소가 저장되며, 이로 인해 참조가 복사되는 것이지, 실제 데이터가 복사되는 것은 아님.
- 스택은 저장된 값에 직접적으로 접근할 수 있는 반면, Heap은 주소를 통해서 접근하기 때문에 더 오래 걸린다.
3. ARC(Automatic Reference Counting)
Swift는 **ARC(Automatic Reference Counting)**라는 메모리 관리 시스템을 사용하여 클래스 인스턴스의 수명을 관리한다. ARC는 각 클래스 인스턴스에 대해 몇 개의 참조가 있는지 추적하여, 참조 카운트가 0이 되면 해당 인스턴스의 메모리를 해제한다.
즉, 참조 카운트가 1 이상이라면 메모리에서 할당 해제되지 않는다.
4. 강한 참조(Strong Reference)와 순환 참조(Circular Reference)
강한 참조(Strong Reference)
ARC에서 참조 카운트를 증가시키는 것은 강한 참조라고 하며, 직접 카운트를 감소(nil)시켜주지 않으면 카운트가 내려가지 않는다. 기본적으로 변수는 클래스 인스턴스를 강하게 참조한다. 즉, 해당 변수가 존재하는 한 인스턴스의 참조 카운트는 증가하며, ARC는 그 인스턴스를 해제하지 않는다.
class Person {
var name: String
init(name: String) {
self.name = name
}
deinit {
print("\\(name) is being deinitialized")
}
}
var person1: Person? = Person(name: "Kim") // 참조 카운트 1
var person2 = person1 // 참조 카운트 증가 - 참조 카운트 2
person1 = nil // person1이 nil이 되어 참조 카운트는 1
person2 = nil // 참조 카운트가 0이 되며, 메모리에서 해제
순환 참조(Circular Reference)
클래스 인스턴스 간에 서로 강한 참조를 할 경우, 참조 카운트가 0이 되지 않아 메모리 누수가 발생할 수 있는데, 이를 **순환 참조(Circular Reference)**라고 한다. 순환 참조는 두 개 이상의 인스턴스가 서로를 참조하면서 해제되지 않는 상태가 되는 상황이다.
class Person {
var name: String
var apartment: Apartment?
init(name: String) {
self.name = name
}
deinit {
print("\\(name) is being deinitialized")
}
}
class Apartment {
var unit: String
var tenant: Person?
init(unit: String) {
self.unit = unit
}
deinit {
print("Apartment \\(unit) is being deinitialized")
}
}
var kim: Person? = Person(name: "Kim") // person 참조 카운트 1
var apt: Apartment? = Apartment(unit: "1A") // apartment 참조 카운트 1
kim?.apartment = apt // apartment 참조 카운트 2
apt?.tenant = kim // 순환 참조 발생 - person 참조 카운트 2
kim = nil // apt로 인한 참조 카운트때문에 Person이 해제되지 않음 - person 참조 카운트 1
apt = nil // kim으로 인한 참조 카운트때문에 Apartment도 해제되지 않음 - apartment 참조 카운트 1
이 경우, Kim과 apt는 서로를 강하게 참조하고 있어 메모리에서 해제되지 않는다. 이러면 더 이상 필요하지 않은 데이터를 유지해야 하기 때문에 메모리 누수가 발생한다.
5. 해결 방법 - 약한 참조(Weak Reference)
약한 참조(weak)
참조 카운트에 영향을 주지 않는 참조 방식. 참조 대상이 메모리에서 해제되면 자동으로 nil이 된다.
class Person {
var name: String
var apartment: Apartment?
init(name: String) {
self.name = name
}
deinit {
print("\\(name) is being deinitialized")
}
}
class Apartment {
var unit: String
weak var tenant: Person? // 약한 참조로 순환 참조 방지
init(unit: String) {
self.unit = unit
}
deinit {
print("Apartment \\(unit) is being deinitialized")
}
}
var kim: Person? = Person(name: "Kim") // Person reference count : 1
var apt: Apartment? = Apartment(unit: "1A") // Apartment reference count : 1
kim?.apartment = apt // Apartment reference count : 2
apt?.tenant = kim // Person reference count : 1
kim = nil // Person 해제 - Apartment reference count : 1
apt = nil // Apartment 해제
이렇게 약한 참조를 사용하면 순환 참조 문제를 해결할 수 있다.
오블완 챌린지 드가자~~
'SOPT' 카테고리의 다른 글
[SOPT] 고량주 2주차 이론 과제 (0) | 2024.11.08 |
---|---|
[SOPT] 35기 iOS 파트 합격 후기 (4) | 2024.09.26 |