목차
메모리 구조
- 코드
- 우리가 만든 코드가 컴파일되어서 CPU가 실행될 수 있는 명령어 형태로 저장되어 있는 것
- 명령어 / 프로그램
- 앱(프로그램)의 모든 코드(Text)
- get-only, read-only (읽을 수만 있는 영역)
- 앱의 프로그램을 한번 짜면 그게 완성이 된 것
- 데이터
- 전역변수 / 타입(static/class) 변수
- 공통으로 공유하기 위한 데이터
- 앱이 실행되는 동안 불변
- 힙
- 동적할당 - 비어있는 메모리를 찾아서 할당하는 개념
- 일반적으로 오랫동안 긴시간 동안 저장
- 크기가 크고, 관리할 필요가 있는 데이터
- 개발자가 잘 관리해야함
- 스택
- 함수실행을 위한 임시적 공간
- 크기가 작고 빠르게 사용하기 위한 데이터
- 알아서 자동 관리됨
💡 Heap(힙) 영역에 할당되는 데이터는 관리를 해야지만, 메모리에서 해제가 됨
💡 할당이 해제되지 않으면 메모리 누수(Memory Leak) 현상이 발생
ARC(Automatic Reference Counting)
Java | Objective - C | Swift |
---|---|---|
GC (Garbage Collector) (런타임에 메모리 감시하는 기법) | MRC (Manual RC) ARC (Automatic RC) | ARC (Automatic RC) |

출처(WWDC)
- retain
- 레퍼런스 카운팅을 하나 올리는 역할
- release
- 메모리를 해제, 레퍼런스 카운팅을 하나 내리는 역할
// MRC(수동 RC관리)와 ARC(자동 RC)
class Dog {
var name: String
var weight: Double
init(name: String, weight: Double) {
self.name = name
self.weight = weight
}
deinit {
print("\(name) 메모리 해제")
}
}
var choco: Dog? = Dog(name: "초코", weight: 15.0) //retain(choco) RC: 1
var bori: Dog? = Dog(name: "보리", weight: 10.0) //retain(bori) RC: 1
choco = nil // RC: 0 , "초코 메모리 해제"
//release(choco)
bori = nil // RC: 0 , "보리 메모리 해제"
//release(bori)
- retain() 할당 → release() 해제
- RC + 1, RC - 1
- 현대적 언어들은 대부분 자동 메모리 관리 모델을 사용
- 스위프트의 경우, 컴파일러가 실제로 “retain() 할당 → release() 해제 코드를 삽입한다고 보면됨
- 컴파일러가 메모리 관리코드를 자동으로 추가해 줌으로써, 프로그램의 메모리 관리에 대한 안정성 증가
- [ARC 모델의 기반: 소유정책과 참조카운팅]
- 소유정책
- 인스턴스 하나이상의 소유자가 있는 경우 메모리에 유지됨
- 소유자가 없으면, 메모리에서 제거
- 참조카운팅
- 인스턴스(나)를 가르키는 소유자수를 카운팅
- 쉽게 말하면, 인스턴스를 가르키고 있는 RC가 1이상이면 메모리에 유지되고, 0이되면 메모리에서 제거
- 소유정책
강한 참조 사이클과 메모리 누수
- 서로 다른 인스턴스가 서로를 가르키는 상황에서는 각각의 Reference count가 1씩 더 증가됨
- 이 상황에선, nil로 변경을 해줘도 Reference Count가 1이 되므로, 메모리 해제가 되지 않아 메모리 누수가 발생
class Dog {
var name: String
var owner: Person?
init(name: String) {
self.name = name
}
deinit {
print("\(name) 메모리 해제")
}
}
class Person {
var name: String
var pet: Dog?
init(name: String) {
self.name = name
}
deinit {
print("\(name) 메모리 해제")
}
}
var bori: Dog? = Dog(name: "보리")
var gildong: Person? = Person(name: "홍길동")
bori?.owner = gildong
gildong?.pet = bori
// 강한 참조 사이클(Strong Reference Cycle)이 일어남
bori = nil // RC : 1, 메모리 해제가 안됨
gildong = nil // RC : 1, 메모리 해제가 안됨
- Memory Leak의 해결방안
- RC를 고려하여, 참조 해제 순서를 주의해서 코드 작성
- 신경쓸 것이 많음 , 실수 가능성
- Weak Reference (약한 참조)
- Unowned Reference (비소유 참조)
- RC를 고려하여, 참조 해제 순서를 주의해서 코드 작성
Strong Reference Cycle
- 강한 참조 싸이클로 인한 메모리 누수(Memory Leak) 해결 방안
Weak Reference(약한 참조) | Unowned Reference(비소유 참조) | |
방식 | weak 키워드 | unowned 키워드 |
예시 | weak var pet: Dog? | unowned var pet: Dog? |
공통점 | ||
차이점 | 소유자에 비해, 보다 짧은 생명주기를 가진 인스턴스를 참조할때 주로 사용 (인스턴스가 nil로 확인가능, nil인 경우 작업을 중단하는 것 가능) |
소유자보다 인스턴스의 생명주기가 더 길거나, 같은 경우에 사용 (인스턴스 nil로 확인불가능, 실제 인스턴스가 해제되었다면 에러 발생) |
- 약한 참조(Weak Reference)
- 참조하고 있던 인스턴스가 사라지면, nil로 초기화 되어있음
- nil로 설정하고 접근하면 → nil
- gildong = nil
- bori?.owner // gildong만 메모리 해제시켰음에도 nil
class Dog {
var name: String
weak var owner: Person? // weak 키워드 ==> 약한 참조
init(name: String) {
self.name = name
}
deinit {
print("\(name) 메모리 해제")
}
}
class Person {
var name: String
weak var pet: Dog? // weak 키워드 ==> 약한 참조
init(name: String) {
self.name = name
}
deinit {
print("\(name) 메모리 해제")
}
}
var bori: Dog? = Dog(name: "보리")
var gildong: Person? = Person(name: "홍길동")
// 강한 참조 사이클이 일어나지 않음
bori?.owner = gildong
gildong?.pet = bori
// 메모리 해제가 잘됨(사실 이 경우 한쪽만 weak으로 선언해도 상관없음)
bori = nil
gildong = nil
- 비소유 참조(Unowned Reference)
class Dog1 {
var name: String
unowned var owner: Person1? // Swift 5.3 이전버전에서는 비소유참조의 경우, 옵셔널 타입 선언이 안되었음
init(name: String) {
self.name = name
}
deinit {
print("\(name) 메모리 해제")
}
}
class Person1 {
var name: String
unowned var pet: Dog1?
init(name: String) {
self.name = name
}
deinit {
print("\(name) 메모리 해제")
}
}
var bori1: Dog1? = Dog1(name: "보리1")
var gildong1: Person1? = Person1(name: "홍길동1")
// 강한 참조 사이클이 일어나지 않음
bori1?.owner = gildong1
gildong1?.pet = bori1
// 메모리 해제가 잘됨(사실 이 경우 한쪽만 unowned로 선언해도 상관없음)
bori1 = nil
gildong1 = nil
- 비소유 참조의 경우, 참조하고 있던 인스턴스가 사라지면, nil로 초기화 되지 않음
- 메모리가 아예 비어 있고 그런 경우에는 에러가 날 수 있음
- 에러가 발생하는 케이스
gildong1 = nil
bori1?.owner // nil로 초기화 되지 않음, 에러 발생
- 에러가 발생하지 않는 케이스
gildong1 = nil
bori1?.owner = nil
bori1?.owner // nil
weak / unowned 사용시 주의점
변수와 타입 | 상수 / 변수 선언 가능 여부 | 옵셔널 타입 가능 여부 | ||
let | var | Optional | Non-Optional | |
strong | O | O | O | O |
weak 키워드 | X (변할 수 있어야함) |
O | O | X (nil을 할당할 수 있어야함) |
unowned 키워드 | O | O | O (기존에는 X) |
O |
- weak var를 주로 사용
변수와 타입 | 상수 / 변수 선언 가능 여부 | 옵셔널 타입 가능 여부 | ||
let | var | Optional | Non-Optional | |
strong (기본 변수 선언시) |
let | var | var pet: Dog? | var pet: Dog |
weak 키워드 | (변할 수 있어야함) |
weak var | weak var pet: Dog? | (nil을 할당할 수 있어야 함) |
unowned 키워드 | unowned let | uowned var | unowned var pet: Dog? (기존에는 X) |
unowned var pet: Dog |
- weak 키워드는 Reference Counting 숫자가 0이 되면 nil로 변할 수 있어야 하기 때문에 let 선언 불가
- 또한 위와 같은 이유로 nil을 할당할 수 있어야 하기 때문에 Optional 타입으로만 선언
반응형
'iOS > Swift' 카테고리의 다른 글
비동기 프로그래밍(About Asynchronous) [1] (0) | 2024.02.16 |
---|---|
Swift No.26 (0) | 2024.02.08 |
Swift No.24 (0) | 2024.02.02 |
Swift No.23 (1) | 2024.01.31 |
Swift No.22 (0) | 2024.01.30 |
메모리 구조
- 코드
- 우리가 만든 코드가 컴파일되어서 CPU가 실행될 수 있는 명령어 형태로 저장되어 있는 것
- 명령어 / 프로그램
- 앱(프로그램)의 모든 코드(Text)
- get-only, read-only (읽을 수만 있는 영역)
- 앱의 프로그램을 한번 짜면 그게 완성이 된 것
- 데이터
- 전역변수 / 타입(static/class) 변수
- 공통으로 공유하기 위한 데이터
- 앱이 실행되는 동안 불변
- 힙
- 동적할당 - 비어있는 메모리를 찾아서 할당하는 개념
- 일반적으로 오랫동안 긴시간 동안 저장
- 크기가 크고, 관리할 필요가 있는 데이터
- 개발자가 잘 관리해야함
- 스택
- 함수실행을 위한 임시적 공간
- 크기가 작고 빠르게 사용하기 위한 데이터
- 알아서 자동 관리됨
💡 Heap(힙) 영역에 할당되는 데이터는 관리를 해야지만, 메모리에서 해제가 됨
💡 할당이 해제되지 않으면 메모리 누수(Memory Leak) 현상이 발생
ARC(Automatic Reference Counting)
Java | Objective - C | Swift |
---|---|---|
GC (Garbage Collector) (런타임에 메모리 감시하는 기법) | MRC (Manual RC) ARC (Automatic RC) | ARC (Automatic RC) |

출처(WWDC)
- retain
- 레퍼런스 카운팅을 하나 올리는 역할
- release
- 메모리를 해제, 레퍼런스 카운팅을 하나 내리는 역할
// MRC(수동 RC관리)와 ARC(자동 RC)
class Dog {
var name: String
var weight: Double
init(name: String, weight: Double) {
self.name = name
self.weight = weight
}
deinit {
print("\(name) 메모리 해제")
}
}
var choco: Dog? = Dog(name: "초코", weight: 15.0) //retain(choco) RC: 1
var bori: Dog? = Dog(name: "보리", weight: 10.0) //retain(bori) RC: 1
choco = nil // RC: 0 , "초코 메모리 해제"
//release(choco)
bori = nil // RC: 0 , "보리 메모리 해제"
//release(bori)
- retain() 할당 → release() 해제
- RC + 1, RC - 1
- 현대적 언어들은 대부분 자동 메모리 관리 모델을 사용
- 스위프트의 경우, 컴파일러가 실제로 “retain() 할당 → release() 해제 코드를 삽입한다고 보면됨
- 컴파일러가 메모리 관리코드를 자동으로 추가해 줌으로써, 프로그램의 메모리 관리에 대한 안정성 증가
- [ARC 모델의 기반: 소유정책과 참조카운팅]
- 소유정책
- 인스턴스 하나이상의 소유자가 있는 경우 메모리에 유지됨
- 소유자가 없으면, 메모리에서 제거
- 참조카운팅
- 인스턴스(나)를 가르키는 소유자수를 카운팅
- 쉽게 말하면, 인스턴스를 가르키고 있는 RC가 1이상이면 메모리에 유지되고, 0이되면 메모리에서 제거
- 소유정책
강한 참조 사이클과 메모리 누수
- 서로 다른 인스턴스가 서로를 가르키는 상황에서는 각각의 Reference count가 1씩 더 증가됨
- 이 상황에선, nil로 변경을 해줘도 Reference Count가 1이 되므로, 메모리 해제가 되지 않아 메모리 누수가 발생
class Dog {
var name: String
var owner: Person?
init(name: String) {
self.name = name
}
deinit {
print("\(name) 메모리 해제")
}
}
class Person {
var name: String
var pet: Dog?
init(name: String) {
self.name = name
}
deinit {
print("\(name) 메모리 해제")
}
}
var bori: Dog? = Dog(name: "보리")
var gildong: Person? = Person(name: "홍길동")
bori?.owner = gildong
gildong?.pet = bori
// 강한 참조 사이클(Strong Reference Cycle)이 일어남
bori = nil // RC : 1, 메모리 해제가 안됨
gildong = nil // RC : 1, 메모리 해제가 안됨
- Memory Leak의 해결방안
- RC를 고려하여, 참조 해제 순서를 주의해서 코드 작성
- 신경쓸 것이 많음 , 실수 가능성
- Weak Reference (약한 참조)
- Unowned Reference (비소유 참조)
- RC를 고려하여, 참조 해제 순서를 주의해서 코드 작성
Strong Reference Cycle
- 강한 참조 싸이클로 인한 메모리 누수(Memory Leak) 해결 방안
Weak Reference(약한 참조) | Unowned Reference(비소유 참조) | |
방식 | weak 키워드 | unowned 키워드 |
예시 | weak var pet: Dog? | unowned var pet: Dog? |
공통점 | ||
차이점 | 소유자에 비해, 보다 짧은 생명주기를 가진 인스턴스를 참조할때 주로 사용 (인스턴스가 nil로 확인가능, nil인 경우 작업을 중단하는 것 가능) |
소유자보다 인스턴스의 생명주기가 더 길거나, 같은 경우에 사용 (인스턴스 nil로 확인불가능, 실제 인스턴스가 해제되었다면 에러 발생) |
- 약한 참조(Weak Reference)
- 참조하고 있던 인스턴스가 사라지면, nil로 초기화 되어있음
- nil로 설정하고 접근하면 → nil
- gildong = nil
- bori?.owner // gildong만 메모리 해제시켰음에도 nil
class Dog {
var name: String
weak var owner: Person? // weak 키워드 ==> 약한 참조
init(name: String) {
self.name = name
}
deinit {
print("\(name) 메모리 해제")
}
}
class Person {
var name: String
weak var pet: Dog? // weak 키워드 ==> 약한 참조
init(name: String) {
self.name = name
}
deinit {
print("\(name) 메모리 해제")
}
}
var bori: Dog? = Dog(name: "보리")
var gildong: Person? = Person(name: "홍길동")
// 강한 참조 사이클이 일어나지 않음
bori?.owner = gildong
gildong?.pet = bori
// 메모리 해제가 잘됨(사실 이 경우 한쪽만 weak으로 선언해도 상관없음)
bori = nil
gildong = nil
- 비소유 참조(Unowned Reference)
class Dog1 {
var name: String
unowned var owner: Person1? // Swift 5.3 이전버전에서는 비소유참조의 경우, 옵셔널 타입 선언이 안되었음
init(name: String) {
self.name = name
}
deinit {
print("\(name) 메모리 해제")
}
}
class Person1 {
var name: String
unowned var pet: Dog1?
init(name: String) {
self.name = name
}
deinit {
print("\(name) 메모리 해제")
}
}
var bori1: Dog1? = Dog1(name: "보리1")
var gildong1: Person1? = Person1(name: "홍길동1")
// 강한 참조 사이클이 일어나지 않음
bori1?.owner = gildong1
gildong1?.pet = bori1
// 메모리 해제가 잘됨(사실 이 경우 한쪽만 unowned로 선언해도 상관없음)
bori1 = nil
gildong1 = nil
- 비소유 참조의 경우, 참조하고 있던 인스턴스가 사라지면, nil로 초기화 되지 않음
- 메모리가 아예 비어 있고 그런 경우에는 에러가 날 수 있음
- 에러가 발생하는 케이스
gildong1 = nil
bori1?.owner // nil로 초기화 되지 않음, 에러 발생
- 에러가 발생하지 않는 케이스
gildong1 = nil
bori1?.owner = nil
bori1?.owner // nil
weak / unowned 사용시 주의점
변수와 타입 | 상수 / 변수 선언 가능 여부 | 옵셔널 타입 가능 여부 | ||
let | var | Optional | Non-Optional | |
strong | O | O | O | O |
weak 키워드 | X (변할 수 있어야함) |
O | O | X (nil을 할당할 수 있어야함) |
unowned 키워드 | O | O | O (기존에는 X) |
O |
- weak var를 주로 사용
변수와 타입 | 상수 / 변수 선언 가능 여부 | 옵셔널 타입 가능 여부 | ||
let | var | Optional | Non-Optional | |
strong (기본 변수 선언시) |
let | var | var pet: Dog? | var pet: Dog |
weak 키워드 | (변할 수 있어야함) |
weak var | weak var pet: Dog? | (nil을 할당할 수 있어야 함) |
unowned 키워드 | unowned let | uowned var | unowned var pet: Dog? (기존에는 X) |
unowned var pet: Dog |
- weak 키워드는 Reference Counting 숫자가 0이 되면 nil로 변할 수 있어야 하기 때문에 let 선언 불가
- 또한 위와 같은 이유로 nil을 할당할 수 있어야 하기 때문에 Optional 타입으로만 선언
반응형
'iOS > Swift' 카테고리의 다른 글
비동기 프로그래밍(About Asynchronous) [1] (0) | 2024.02.16 |
---|---|
Swift No.26 (0) | 2024.02.08 |
Swift No.24 (0) | 2024.02.02 |
Swift No.23 (1) | 2024.01.31 |
Swift No.22 (0) | 2024.01.30 |