Swift 의 메모리 관리 방식인 ARC(Automatic Reference Counting)에 대해서 알아봅니다.
예시와 도표/다이어그램을 통해 쉽게 이해할 수 있습니다.
[목차]
1. 메모리 관리의 필요성
2. Reference Counting 의 이해
3. Swift 에서의 구현 & memory leak 해결하기
더 많은 내용 ☞ https://daheenallwhite.github.io
6. 6
메모리에서 class instance 생명 주기
1. Allocation : 인스턴스를 위한 메모리 공간 찾기 🔍
2. Initialization : init()
3. 인스턴스 사용
4. Deinitialization : deinit() (메모리 회수 직전)
5. Deallocation
7. !7
Reference Counting 적용한 코드
class Point {
var x, y: Int
init(x: Int, y: Int) {
self.x = x
self.y = y
}
}
let point1 = Point(x: 0, y: 0)
let point2 = point1
// use point1
// use point2
작성한 코드
class Point {
var refCount: Int
var x, y: Int
init(x: Int, y: Int) {
self.x = x
self.y = y
}
}
let point1 = Point(x: 0, y: 0)
let point2 = point1
retain(point2)
// use point1
release(point1)
// use point2
release(point2)
Swift 컴파일러가 ARC 적용하여 생성한 코드
8. !8
Reference Counting - Runtime
class Point {
var refCount: Int
var x, y: Int
init(x: Int, y: Int) {
self.x = x
self.y = y
}
}
let point1 = Point(x: 0, y: 0)
let point2 = point1
retain(point2)
// use point1
release(point1)
// use point2
release(point2)
HeapStack
point1
point2
9. !9
class Point {
var refCount: Int
var x, y: Int
init(x: Int, y: Int) {
self.x = x
self.y = y
}
}
let point1 = Point(x: 0, y: 0)
let point2 = point1
retain(point2)
// use point1
release(point1)
// use point2
release(point2)
HeapStack
point1
point2 refCount: 1
x: 0
y: 0
Reference Counting - Runtime
10. !10
class Point {
var refCount: Int
var x, y: Int
init(x: Int, y: Int) {
self.x = x
self.y = y
}
}
let point1 = Point(x: 0, y: 0)
let point2 = point1
retain(point2)
// use point1
release(point1)
// use point2
release(point2)
HeapStack
point1
point2 refCount: 1
x: 0
y: 0
Reference Counting - Runtime
11. !11
class Point {
var refCount: Int
var x, y: Int
init(x: Int, y: Int) {
self.x = x
self.y = y
}
}
let point1 = Point(x: 0, y: 0)
let point2 = point1
retain(point2)
// use point1
release(point1)
// use point2
release(point2)
HeapStack
point1
point2 refCount: 2
x: 0
y: 0
Reference Counting - Runtime
12. !12
Reference Counting - Runtime
class Point {
var refCount: Int
var x, y: Int
init(x: Int, y: Int) {
self.x = x
self.y = y
}
}
let point1 = Point(x: 0, y: 0)
let point2 = point1
retain(point2)
// use point1
release(point1)
// use point2
release(point2)
HeapStack
point1
point2 refCount: 1
x: 0
y: 0
13. !13
Reference Counting - Runtime
class Point {
var refCount: Int
var x, y: Int
init(x: Int, y: Int) {
self.x = x
self.y = y
}
}
let point1 = Point(x: 0, y: 0)
let point2 = point1
retain(point2)
// use point1
release(point1)
// use point2
release(point2)
HeapStack
point1
point2 refCount: 1
x: 0
y: 0
14. !14
class Point {
var refCount: Int
var x, y: Int
init(x: Int, y: Int) {
self.x = x
self.y = y
}
}
let point1 = Point(x: 0, y: 0)
let point2 = point1
retain(point2)
// use point1
release(point1)
// use point2
release(point2)
HeapStack
point1
point2 refCount: 0
x: 0
y: 0
Reference Counting - Runtime
15. !15
class Point {
var refCount: Int
var x, y: Int
init(x: Int, y: Int) {
self.x = x
self.y = y
}
}
let point1 = Point(x: 0, y: 0)
let point2 = point1
retain(point2)
// use point1
release(point1)
// use point2
release(point2)
HeapStack
point1
point2 refCount: 0
x: 0
y: 0
Reference Counting - Runtime
16. !16
class Point {
var refCount: Int
var x, y: Int
init(x: Int, y: Int) {
self.x = x
self.y = y
}
}
let point1 = Point(x: 0, y: 0)
let point2 = point1
retain(point2)
// use point1
release(point1)
// use point2
release(point2)
HeapStack
Reference Counting - Runtime
18. Strong Reference
Class TabletClass User
class User {
let name: String
var tablet: Tablet?
init(name: String) {
self.name = name
print("생성: User (self.name)")
}
deinit {
print("소멸: User (self.name)")
}
}
class Tablet {
let model: String
var owner: User?
init(model: String) {
self.model = model
print("생성: Phone (self.model)")
}
deinit {
print("소멸: Phone (self.model)")
}
}
!18
🧒
19. !19
Strong Reference
var iPadProvar dana
<User Instance>
name: “Dana”
tablet: <Tablet Instance>
<Tablet Instance>
model: “iPad Pro”
owner: <User Instance>
var dana: User? = User(name: "Dana")
var iPadPro: Tablet? = Tablet(model: "iPad Pro")
20. !20
Strong Reference
var iPadProvar 흰
<Tablet Instance>
model: “iPad Pro”
owner: <User Instance>
var dana: User? = User(name: "Dana")
var iPadPro: Tablet? = Tablet(model: "iPad Pro")
// 새로 샀다!!
dana!.tablet = iPadPro
iPadPro!.owner = dana
var dana
<User Instance>
name: “Dana”
tablet: <Tablet Instance>
21. !21
Strong Reference Cycle
var iPadProvar 흰
<Tablet Instance>
model: “iPad Pro”
owner: <User Instance>
!21
strong strong
strong
strong
var dana
<User Instance>
name: “Dana”
tablet: <Tablet Instance>
22. !22
Strong Reference Cycle
var iPadProvar 흰
<Tablet Instance>
model: “iPad Pro”
owner: <User Instance>
!22
strong strong
strong
strong
// 박살남.. 😇 😭
iPadPro = nil
var dana
<User Instance>
name: “Dana”
tablet: <Tablet Instance>
23. !23
Strong Reference Cycle
var iPadProvar 흰
<Tablet Instance>
model: “iPad Pro”
owner: <User Instance>
!23
strong
strong
strong
var dana
<User Instance>
name: “Dana”
tablet: <Tablet Instance>
// 박살남.. 😇 😭
iPadPro = nil
24. !24
Strong Reference Cycle
var iPadProvar 흰
<Tablet Instance>
model: “iPad Pro”
owner: <User Instance>
!24
strong
strong
strong
// 박살남.. 😇 😭
iPadPro = nil
// 집 나감 🤬
dana = nil
var dana
<User Instance>
name: “Dana”
tablet: <Tablet Instance>
25. !25
Strong Reference Cycle
var iPadProvar 흰
<Tablet Instance>
model: “iPad Pro”
owner: <User Instance>
!25
strong
strong
var dana
<User Instance>
name: “Dana”
tablet: <Tablet Instance>
// 박살남.. 😇 😭
iPadPro = nil
// 집 나감 🤬
dana = nil
26. !26
Strong Reference Cycle
var iPadProvar 흰
<Tablet Instance>
model: “iPad Pro”
owner: <User Instance>
!26
strong
strong
// 박살남.. 😇 😭
iPadPro = nil
// 집 나감 🤬
흰 = nil
var dana
<User Instance>
name: “Dana”
tablet: <Tablet Instance>
// 박살남.. 😇 😭
iPadPro = nil
// 집 나감 🤬
dana = nil
27. !27
Strong Reference Cycle 해결
원인
Reference count 를 0으로 만들지 못하게 함
두 인스턴스의 상대적인 수명을 알 수 없음
해결
Strong Reference 일 때만 reference count를 세자!
상대적인 수명에 대한 정보를 미리 알려주자!
28. 해결 방법 - weak & unowned references
weak unowned
수명>
weak
<=
unowned
해당 instance에서 참조하는 instance가
더 적은 시간동안 메모리에 있는 경우
해당 instance에서 참조하는 instance가
더 오래 혹은 같은 시간동안
메모리에 있어야 하는 경우
참조하는 속성 값 == nil
인스턴스가 있을수도/없을수도
Optional
상대 인스턴스에 의존관계인 경우
항상 있어야 할 때
Non-optional
29. 해결 - Weak Reference
Class TabletClass User
class User {
let name: String
weak var tablet: Tablet?
init(name: String) {
self.name = name
print("생성: User (self.name)")
}
deinit {
print("소멸: User (self.name)")
}
}
class Tablet {
let model: String
var owner: User?
init(model: String) {
self.model = model
print("생성: Tablet (self.model)")
}
deinit {
print("소멸: Tablet (self.model)")
}
}
!29
🧒
30. 해결 - Weak Reference
!30!30!30
strong strong
<User Instance>
name: “Dana”
tablet: <Tablet Instance>
weak
<Tablet Instance>
model: “iPad Pro”
owner: <User Instance>
strong
var iPadProvar dana
var dana: User? = User(name: "Dana")
var iPadPro: Tablet? = Tablet(model: "iPad Pro")
// 새로 샀다!!
dana!.tablet = iPadPro
iPadPro!.owner = dana
// 박살남
iPadPro = nil
31. !31!31!31
strong
weak
<Tablet Instance>
model: “iPad Pro”
owner: <User Instance>
strong
var iPadProvar 흰
<User Instance>
name: “Dana”
tablet: <Tablet Instance>
var dana
해결 - Weak Reference
var dana: User? = User(name: "Dana")
var iPadPro: Tablet? = Tablet(model: "iPad Pro")
// 새로 샀다!!
dana!.tablet = iPadPro
iPadPro!.owner = dana
// 박살남
iPadPro = nil
32. !32!32!32
strong
weak
var iPadPro
<User Instance>
name: “Dana”
tablet: <Tablet Instance>
var dana
해결 - Weak Reference
var dana: User? = User(name: "Dana")
var iPadPro: Tablet? = Tablet(model: "iPad Pro")
// 새로 샀다!!
dana!.tablet = iPadPro
iPadPro!.owner = dana
// 박살남
iPadPro = nil
33. !33!33!33
strong
var iPadPro
<User Instance>
name: “Dana”
tablet: <Tablet Instance>
var dana
nil
// 집 나감 🤬
dana = nil
해결 - Weak Reference
var dana: User? = User(name: "Dana")
var iPadPro: Tablet? = Tablet(model: "iPad Pro")
// 새로 샀다!!
dana!.tablet = iPadPro
iPadPro!.owner = dana
// 박살남
iPadPro = nil
34. !34!34!34
strong
var iPadPro
<User Instance>
name: “Dana”
tablet: <Tablet Instance>
var dana
nil
해결 - Weak Reference
// 집 나감 🤬
dana = nil
var dana: User? = User(name: "Dana")
var iPadPro: Tablet? = Tablet(model: "iPad Pro")
// 새로 샀다!!
dana!.tablet = iPadPro
iPadPro!.owner = dana
// 박살남
iPadPro = nil
35. !35!35!35
var iPadPro
<User Instance>
name: “Dana”
tablet: <Tablet Instance>
var dana
nil
해결 - Weak Reference
// 집 나감 🤬
dana = nil
var dana: User? = User(name: "Dana")
var iPadPro: Tablet? = Tablet(model: "iPad Pro")
// 새로 샀다!!
dana!.tablet = iPadPro
iPadPro!.owner = dana
// 박살남
iPadPro = nil
36. !36!36!36
var iPadProvar dana
해결 - Weak Reference
// 집 나감 🤬
dana = nil
var dana: User? = User(name: "Dana")
var iPadPro: Tablet? = Tablet(model: "iPad Pro")
// 새로 샀다!!
dana!.tablet = iPadPro
iPadPro!.owner = dana
// 박살남
iPadPro = nil
37. !37!37!37
Unowned Reference
Class NetflixMembershipClass User
class NetflixMembership {
let plan: String
let owner: User
init(plan: String, owner: User) {
self.plan = plan
self.owner = owner
print("생성: (self.plan) 구독")
}
deinit {
print("소멸: (self.plan) 구독")
}
}
class User {
let name: String
var netflixMembership:
NetflixMembership?
init(name: String) {
self.name = name
print("생성: User (self.name)")
}
deinit {
print("소멸: User (self.name)")
}
}
unowned let owner: User
🧒
38. !38!38!38
해결 - Unowned Reference
var dana
var dana: User? = User(name: “Dana")
dana!.netflixMembership = NetflixMembership(plan: "Premium", owner: dana!)
39. !39!39!39
var 흰
strong
<User Instance>
name: “Dana”
streamingMembership:
<StreamingMembership Instance>
var dana
해결 - Unowned Reference
var dana: User? = User(name: “Dana")
dana!.netflixMembership = NetflixMembership(plan: "Premium", owner: dana!)
40. !40!40!40
strong
<NetflixMembership Instance>
Plan : “Premium”
owner: <User Instance>
strong
<User Instance>
name: “Dana”
streamingMembership:
<StreamingMembership Instance>
var dana
해결 - Unowned Reference
var dana: User? = User(name: “Dana")
dana!.netflixMembership = NetflixMembership(plan: "Premium", owner: dana!)
41. !41!41!41
strong
<NetflixMembership Instance>
Plan : “Premium”
owner: <User Instance>
strong
unowned
<User Instance>
name: “Dana”
streamingMembership:
<StreamingMembership Instance>
var dana
해결 - Unowned Reference
var dana: User? = User(name: “Dana")
dana!.netflixMembership = NetflixMembership(plan: "Premium", owner: dana!)
42. !42!42!42
strong
<NetflixMembership Instance>
Plan : “Premium”
owner: <User Instance>
strong
unowned
// 탈퇴..
Dana = nil
<User Instance>
name: “Dana”
streamingMembership:
<StreamingMembership Instance>
var dana
var dana: User? = User(name: “Dana")
dana!.netflixMembership = NetflixMembership(plan: "Premium", owner: dana!)
해결 - Unowned Reference
43. !43!43!43
<NetflixMembership Instance>
Plan : “Premium”
owner: <User Instance>
strong
unowned
// 탈퇴..
Dana = nil
<User Instance>
name: “Dana”
streamingMembership:
<StreamingMembership Instance>
var dana
var dana: User? = User(name: “Dana")
dana!.netflixMembership = NetflixMembership(plan: "Premium", owner: dana!)
해결 - Unowned Reference
44. !44!44!44
<NetflixMembership Instance>
Plan : “Premium”
owner: <User Instance>
unowned
// 탈퇴..
Dana = nil
var dana
var dana: User? = User(name: “Dana")
dana!.netflixMembership = NetflixMembership(plan: "Premium", owner: dana!)
해결 - Unowned Reference
45. !45!45!45
// 탈퇴..
Dana = nil
var dana
var dana: User? = User(name: “Dana")
dana!.netflixMembership = NetflixMembership(plan: "Premium", owner: dana!)
해결 - Unowned Reference
46. !46
Strong Reference Cycle for Closure
class User {
let name: String
var nickName: String?
init(name: String, nickName: String? = nil) {
self.name = name
self.nickName = nickName
}
lazy var completeUserName: () -> String = {
if let nickName = self.nickName {
return "(self.name) ((nickName))"
} else {
return "(self.name)"
}
}
deinit {
print("탈퇴: User (self.name)")
}
}
47. class User {
let name: String
var nickName: String?
init(name: String, nickName: String? = nil) {
self.name = name
self.nickName = nickName
}
lazy var completeUserName: () -> String = {
if let nickName = self.nickName {
return "(self.name) ((nickName))"
} else {
return "(self.name)"
}
}
deinit {
print("탈퇴: User (self.name)")
}
}
!47
Strong Reference Cycle for Closure
var dana: User? =
User(name: "Dana", nickName: "white")
print(dana!.completeUserName())
dana = nil
var dana
<User Instance>
name: “Dana”
nickName: “white”
completeUserName:
( ) -> String
( ) -> String
self.name self.nickName
strong
strong
strong
48. !48
Strong Reference Cycle for Closure
var 흰
( ) -> String
self.name self.nickName
strong
class User {
let name: String
var nickName: String?
init(name: String, nickName: String? = nil) {
self.name = name
self.nickName = nickName
}
lazy var completeUserName: () -> String = {
if let nickName = self.nickName {
return "(self.name) ((nickName))"
} else {
return "(self.name)"
}
}
deinit {
print("탈퇴: User (self.name)")
}
}
strong
var dana
<User Instance>
name: “Dana”
nickName: “white”
completeUserName:
( ) -> String
var dana: User? =
User(name: "Dana", nickName: "white")
print(dana!.completeUserName())
dana = nil
49. class User {
let name: String
var nickName: String?
init(name: String, nickName: String? = nil) {
self.name = name
self.nickName = nickName
}
lazy var completeUserName: () -> String = {
[unowned self] in
if let nickName = self.nickName {
return "(self.name) ((nickName))"
} else {
return "(self.name)"
}
}
deinit {
print("탈퇴: User (self.name)")
}
}
!49
var 흰
( ) -> String
self.name self.nickName
unowned
strong
strong
해결 - Capture List
var dana
<User Instance>
name: “Dana”
nickName: “white”
completeUserName:
( ) -> String
var dana: User? =
User(name: "Dana", nickName: "white")
print(dana!.completeUserName())
dana = nil
50. class User {
let name: String
var nickName: String?
init(name: String, nickName: String? = nil) {
self.name = name
self.nickName = nickName
}
lazy var completeUserName: () -> String = {
[unowned self] in
if let nickName = self.nickName {
return "(self.name) ((nickName))"
} else {
return "(self.name)"
}
}
deinit {
print("탈퇴: User (self.name)")
}
}
!50
var 흰
( ) -> String
self.name self.nickName
unowned
strong
var dana
<User Instance>
name: “Dana”
nickName: “white”
completeUserName:
( ) -> String
해결 - Capture List
var dana: User? =
User(name: "Dana", nickName: "white")
print(dana!.completeUserName())
dana = nil
51. class User {
let name: String
var nickName: String?
init(name: String, nickName: String? = nil) {
self.name = name
self.nickName = nickName
}
lazy var completeUserName: () -> String = {
[unowned self] in
if let nickName = self.nickName {
return "(self.name) ((nickName))"
} else {
return "(self.name)"
}
}
deinit {
print("탈퇴: User (self.name)")
}
}
!51
( ) -> String
self.name self.nickName
unowned
var dana
해결 - Capture List
var dana: User? =
User(name: "Dana", nickName: "white")
print(dana!.completeUserName())
dana = nil
52. class User {
let name: String
var nickName: String?
init(name: String, nickName: String? = nil) {
self.name = name
self.nickName = nickName
}
lazy var completeUserName: () -> String = {
[unowned self] in
if let nickName = self.nickName {
return "(self.name) ((nickName))"
} else {
return "(self.name)"
}
}
deinit {
print("탈퇴: User (self.name)")
}
}
!52
var dana
해결 - Capture List
var dana: User? =
User(name: "Dana", nickName: "white")
print(dana!.completeUserName())
dana = nil
53. !53
Summary 📌
메모리 관리 필요성
Reference Counting : 필요로 하는 애들의 수를 세자
Swift Compiler, ARC / MRC
Strong Reference Cycle : 메모리 공간 낭비의 원인
Weak / Unowned : 상대적 수명에 대한 정보를 제공해주자
Closure : of Reference Type. Capture List
54. !54
Strong / Weak / Unowned
raywenderlich - ARC and Memory Management in Swift
55. !55
Reference 📗
✦ Swift Language Guide - Automatic Reference Counting
✦ raywenderlich - ARC and Memory Management in Swift
✦ WWDC 2015 - Optimizing Swift Performance
✦ WWDC 2016 - Understanding Swift Performance
✦ ARC Release Note
✦ Soulpark blog - Automatic Reference Counting (ARC)
56. !56
더 알아보기 🔍
✦ Value type이 메모리에서 관리되는 방식
✦ Understanding Swift Performance - WWDC 2016
✦ 메모리 관리 방식을 고려한 Swift 성능 올리기
✦ Optimizing Swift Performance - WWDC 2015
✦ Understanding Swift Performance - WWDC 2016
✦ Object-C 에서 MRC/ARC
✦ ARC obj-c
✦ Transition to ARC (no longer supported?)
✦ 고급언어의 컴파일러와 프로그램의 로딩
✦ Garbage Collector 와 ARC 의 차이