1. Memory Safety πŸ‘©β€πŸ’»

기본적으둜 Swift λŠ” μ½”λ“œμ—μ„œ μ•ˆμ „ν•˜μ§€ μ•Šμ€ μž‘λ™μ΄ λ°œμƒν•˜λŠ” 것을 λ°©μ§€ν•œλ‹€. 예λ₯Ό λ“€λ©΄, Initialization 이전에 Variables 에 μ ‘κ·Όν•˜κΈ°, Deallocated 이후 λ©”λͺ¨λ¦¬μ— μ ‘κ·Όν•˜κΈ°, Array 의 λ²”μœ„ 체크(out-of-bounds)와 같은 것듀이닀.

λ˜ν•œ Swift λŠ” λ™μΌν•œ λ©”λͺ¨λ¦¬ 곡간에 λŒ€ν•œ Multiple Accesses λ°œμƒμ‹œ, ν•΄λ‹Ή λ©”λͺ¨λ¦¬λ₯Ό μˆ˜μ •μ€‘μΈ μ½”λ“œμ—κ²Œ Exclusive Access(독점적인 μ ‘κ·Ό)을 ν•˜λ„λ‘ ν•΄ Conflicts이 λ°œμƒλ˜μ§€ μ•Šλ„λ‘ ν•œλ‹€.

Swift λŠ” λ©”λͺ¨λ¦¬λ₯Ό μžλ™μœΌλ‘œ κ΄€λ¦¬ν•˜κΈ° λ•Œλ¬Έμ— λŒ€λΆ€λΆ„μ˜ κ²½μš°μ— λ©”λͺ¨λ¦¬ 접근에 λŒ€ν•΄ 생각할 ν•„μš”κ°€ μ—†λ‹€. κ·ΈλŸ¬λ‚˜, Conflicts이 λ°œμƒν•  κ°€λŠ₯성이 μžˆλŠ” κ²½μš°μ— λŒ€ν•΄ μ•Œμ•„μ•Ό λ©”λͺ¨λ¦¬ 접근에 λŒ€ν•œ Conflicting Access λ₯Ό ν”Όν•  수 μžˆμœΌλ―€λ‘œ 이것을 μ΄ν•΄ν•˜λŠ” 것은 μ€‘μš”ν•˜λ‹€. λ§Œμ•½ 이λ₯Ό ν”Όν•˜μ§€ λͺ»ν•΄ Conflicts 을 μΌμœΌν‚¬ 수 μžˆλŠ” μ½”λ“œκ°€ ν¬ν•¨λ˜μ–΄ μžˆλ‹€λ©΄, Compile-time Error λ˜λŠ” Runtime Errorκ°€ λ°œμƒν•œλ‹€.

2. Memory Access πŸ‘©β€πŸ’»

1. Understanding Conflicting Access to Memory

λ©”λͺ¨λ¦¬μ— μ ‘κ·Όν•˜λŠ” 것은 λ³€μˆ˜μ— 값을 μ„€μ •ν•˜κ±°λ‚˜ ν•¨μˆ˜μ— arguments λ₯Ό μ „λ‹¬ν•˜λŠ” 것과 같은 μž‘λ™μ„ ν•  λ•Œ λ°œμƒν•œλ‹€. λ‹€μŒ μ½”λ“œλŠ” λ©”λͺ¨λ¦¬ μ ‘κ·Όμ˜ Read Access와 Write Access에 λŒ€ν•œ μ˜ˆλ‹€.

// A write access to the memory where one is stored.
var one = 1

// A read access from the memory where one is stored.
print("We're number \(one)!")

μ½”λ“œμ˜ μ„œλ‘œ λ‹€λ₯Έ 뢀뢄이 λ©”λͺ¨λ¦¬μ˜ 동일 μœ„μΉ˜μ— λ™μ‹œμ— μ ‘κ·Όν•˜λ €λŠ” 경우 μ˜ˆμΈ‘ν•  수 μ—†κ±°λ‚˜ 일관성 μ—†λŠ” μž‘λ™μ΄ λ°œμƒν•  수 있고, 이둜 인해 Conflicting Accessκ°€ λ°œμƒν•  수 μžˆλ‹€. Swift μ—λŠ” μ½”λ“œμ˜ μ—¬λŸ¬ 라인에 걸쳐 μžˆλŠ” 값을 μˆ˜μ •ν•˜λŠ” 방법이 μžˆμ–΄, 자체 μˆ˜μ • 쀑에 값에 접근을 μ‹œλ„ν•  수 μžˆλ‹€. λ‹€μŒ μ½”λ“œλŠ” 이런 상황에 λŒ€ν•œ μ˜ˆμ‹œλ₯Ό 보여쀀닀.

Memory Shopping

μ˜ˆμ‚° μ—…λ°μ΄νŠΈλŠ” 2λ‹¨κ³„λ‘œ 이루어진닀.

  • 1단계 : μ•„μ΄ν…œμ„ λ‹΄λŠ”λ‹€.
  • 2단계 : Total 을 μ—…λ°μ΄νŠΈ ν•œλ‹€.

2λ‹¨κ³„κΉŒμ§€ μ’…λ£Œλ˜μ–΄ μ˜ˆμ‚° μ—…λ°μ΄νŠΈκ°€ μ™„λ£Œλœ ν›„μ—λŠ” μ˜¬λ°”λ₯Έ 값을 얻을 수 μžˆλ‹€. ν•˜μ§€λ§Œ 1λ‹¨κ³„λ§Œ μ™„λ£Œλœ μ‹œμ μ— Total 에 μ ‘κ·Όν•  경우, μž„μ‹œμ μœΌλ‘œ μ˜¬λ°”λ₯΄μ§€ μ•Šμ€ 값을 μ–»λŠ”λ‹€.

ν•˜μ§€λ§Œ μ˜¬λ°”λ₯΄μ§€ μ•Šμ€ 값을 μ–»λŠ”λ‹€λŠ” 것은 그림상 β€˜During’ 쑰각 ν•˜λ‚˜λ§Œ λ³΄μ•˜μ„ λ•Œ 이야기일 뿐이닀. ν”„λ‘œκ·Έλž˜λ° κ΄€μ μ—μ„œ 보면 이와 같은 문제λ₯Ό ν•΄κ²°ν•˜λŠ” 방법은 μ—¬λŸ¬ 가지가 μ‘΄μž¬ν•˜λŠ”λ°, κΈ°μ‘΄ Total κ³Ό μ—…λ°μ΄νŠΈ 된 Total 쀑 μ–΄λ–€ 값을 μ›ν•˜λŠ”μ§€μ— 따라 β€˜$5’ κ°€ 정닡이 될 μˆ˜λ„ 있고, β€˜$320’ 이 정닡이 될 μˆ˜λ„ μžˆλ‹€. λ”°λΌμ„œ Conflicting Accessλ₯Ό 고치기 전에 μž‘λ™μ΄ μˆ˜ν–‰ν•˜κ³ μž ν•˜λŠ” μ˜λ„λ₯Ό λͺ…ν™•νžˆ νŒŒμ•…ν•˜λŠ” 것이 μ€‘μš”ν•˜λ‹€.

Concurrent Code λ˜λŠ” Multithreaded Code λ₯Ό μž‘μ„±ν•  경우 Conflicting Access to Memoryλ₯Ό 자주 μ ‘ν•  수 μžˆλ‹€. ν•˜μ§€λ§Œ Conflicting Access λŠ” Single Threadμ—μ„œλ„ λ°œμƒν•  수 μžˆλ‹€. 이 κΈ€μ—μ„œ μ„€λͺ…ν•˜λŠ” Conflicts κ°€ 이에 ν•΄λ‹Ήν•œλ‹€.

  • Conflicting Access to Memory (Single Thread) : Conflicts 이 λ°œμƒν•  경우 Swift λŠ” 이λ₯Ό 감지해 Compile-time Error λ˜λŠ” Runtime Error κ°€ λ°œμƒν•˜λ„λ‘ 보μž₯ν•œλ‹€.
  • Conflicting Access to Memory (Multithread) : Thread Sanitizer λ₯Ό μ‚¬μš©ν•΄ Threads 사이에 λ°œμƒν•˜λŠ” Conflicts 을 κ°μ§€ν•œλ‹€.

2. Characteristics of Memory Access

Conflicting Access μ—μ„œ κ³ λ €ν•΄μ•Ό ν•  Memory Access 의 3가지 νŠΉμ„±μ΄ μžˆλ‹€.

  1. Read Access 인가? Write Access 인가?
  2. Access 지속 μ‹œκ°„
  3. Access λ˜λŠ” λ©”λͺ¨λ¦¬ μœ„μΉ˜

특히 λ‹€μŒ 쑰건을 λ§Œμ‘±ν•˜λŠ” 2개의 Accesses κ°€ μžˆλ‹€λ©΄ Conflicts κ°€ λ°œμƒν•œλ‹€.

  • 적어도 ν•˜λ‚˜μ˜ Write Access λ˜λŠ” Nonatomic Access
  • λ©”λͺ¨λ¦¬μ˜ 같은 μœ„μΉ˜μ— μ ‘κ·Ό
  • μ ‘κ·Ό κΈ°κ°„(duration)이 쀑볡

일반적으둜 Read Access 와 Write Access 의 μ°¨μ΄λŠ” λͺ…ν™•ν•˜λ‹€. Write Access λŠ” λ©”λͺ¨λ¦¬μ˜ μœ„μΉ˜λ₯Ό λ³€κ²½ν•˜μ§€λ§Œ, Read Access λŠ” 그렇지 μ•Šλ‹€. λ©”λͺ¨λ¦¬μ˜ μœ„μΉ˜λŠ” Variables, Constants, Properties 와 같은 μ ‘κ·Ό 쀑인 ν•­λͺ©μ„ λ‚˜νƒ€λ‚Έλ‹€. λ©”λͺ¨λ¦¬ μ ‘κ·Ό 기간은 μˆœκ°„μ (instantaneous)μ΄κ±°λ‚˜ μž₯기적(long-term)이닀.

연산이 C atomic operations 만 μ‚¬μš©ν•˜λŠ” 경우 Atomic이고, 그렇지 μ•ŠμœΌλ©΄ Nonatomic이닀. μ΄λŸ¬ν•œ ν•¨μˆ˜ λͺ©λ‘μ€ stdatomic.3 νŽ˜μ΄μ§€λ₯Ό μ°Έκ³ ν•œλ‹€.

Access κ°€ μ‹œμž‘λ˜κ³  μ’…λ£Œλ˜κΈ° μ „κΉŒμ§€ λ‹€λ₯Έ μ½”λ“œλ₯Ό μ‹€ν–‰ν•  수 μ—†λŠ” 경우, 접근은 μ¦‰μ‹œ(instantaneous) 이루어진닀. 일반적으둜 2개의 Instantaneous Access은 λ™μ‹œμ— λ°œμƒν•  수 μ—†λ‹€. ν•˜μ§€λ§Œ λŒ€λΆ€λΆ„μ˜ λ©”λͺ¨λ¦¬ 접근은 μ¦‰κ°μ μœΌλ‘œ λ°˜μ‘ν•˜λ©°, μ•„λž˜ μ½”λ“œ 리슀트의 λͺ¨λ“  Read Access 와 Write Access λŠ” μ¦‰μ‹œ 이루어진닀(λ™μ‹œμ— μ΄λ£¨μ–΄μ§€λŠ” 것을 λ§ν•˜λŠ” 것은 μ•„λ‹ˆλ‹€. λ‘˜μ΄ 순차적으둜 즉각적인 λ°˜μ‘μ„ λ³΄μΈλ‹€λŠ” 것이닀).

func oneMore(than number: Int) -> Int {
    return number + 1
}

var myNumber = 1
myNumber = oneMore(than: myNumber)
print(myNumber) // 2

κ·ΈλŸ¬λ‚˜ λ‹€λ₯Έ μ½”λ“œμ˜ 싀행에 걸쳐 μžˆλŠ” Long-term Accesses 에 μ ‘κ·Όν•˜λŠ” 방법은 μ—¬λŸ¬ 가지가 μžˆλ‹€. Instantaneous Access와 Long-term Access의 차이점은 Long-term Access λŠ” μ‹œμž‘λ˜κ³  μ’…λ£Œλ˜κΈ° 전에 λ‹€λ₯Έ μ½”λ“œκ°€ 싀행될 수 μžˆλ‹€λŠ” 것이닀. 이것을 Overlap이라 ν•œλ‹€.

  • Instantaneous Access : Access κ°€ μ‹œμž‘λ˜κ³  μ’…λ£Œλ˜κΈ° μ „κΉŒμ§€ λ‹€λ₯Έ μ½”λ“œκ°€ 싀행될 수 μ—†λ‹€.
  • Long-term Access : Overlap이 κ°€λŠ₯ν•΄ Access κ°€ μ‹œμž‘λ˜κ³  μ’…λ£Œλ˜κΈ° μ „κΉŒμ§€ λ‹€λ₯Έ Instantaneous Access λ˜λŠ” Long-term Access κ°€ 싀행될 수 μžˆλ‹€.

Overlapping AccessesλŠ” 주둜 ν•¨μˆ˜λ‚˜ λ©”μ„œλ“œμ—μ„œ in-out λ˜λŠ” mutating을 μ‚¬μš©ν•˜λŠ” μ½”λ“œμ—μ„œ 주둜 λ‚˜νƒ€λ‚œλ‹€.


3. Conflicting Access to In-Out Parameters πŸ‘©β€πŸ’»

ν•¨μˆ˜λŠ” λͺ¨λ“  In-Out Parameters 에 Long-term Write Access λ₯Ό κ°–κ³  μžˆλ‹€. In-Out Parameters 에 λŒ€ν•œ Write Access λŠ” λ‚˜λ¨Έμ§€ λͺ¨λ“  Non-In-Out Parameters κ°€ ν‰κ°€λœ 후에 μ‹œμž‘λ˜μ–΄ ν•¨μˆ˜κ°€ ν˜ΈμΆœλ˜λŠ” λ™μ•ˆ μ§€μ†λœλ‹€. In-Out Parameters κ°€ μ—¬λŸ¬ 개인 경우 Write Access λŠ” Parameters 의 μˆœμ„œμ™€ λ™μΌν•˜κ²Œ 이루어진닀.

  • Read Access 와 Write Access κ°€ λ™μ‹œμ— 이루어지지 μ•ŠλŠ” 경우
var someNumber = 7

func incrementByTen(_ number: inout Int) {
    number += 10
}

incrementByTen(&someNumber)
print(someNumber) // 7
  • Long-term Write Access λ₯Ό κ°–λŠ” In-Out Parameters 와 ν•¨μˆ˜ λ‚΄λΆ€μ˜ λ‹€λ₯Έ Read Access κ°€ λ™μ‹œμ— 이루어진 경우(same duration)
var someNumber = 7

func incrementByTen(_ number: inout Int) {
    print(someNumber)   // error: simultaneous access
    number += 10
}

incrementByTen(&someNumber) // error: Execution was interrupted, reason: signal SIGABRT.
print(someNumber)


λ‹€μŒκ³Ό 같은 ν•¨μˆ˜λ₯Ό μƒκ°ν•΄λ³΄μž.

var stepSize = 1

func increment(_ number: inout Int) {
    number += stepSize
}

increment(&stepSize)    // error: Execution was interrupted, reason: signal SIGABRT.

μœ„μ—μ„œ μ‚΄νŽ΄λ³Έ 것과 λ§ˆμ°¬κ°€μ§€λ‘œ Read Access 와 Write Access κ°€ λ™μ‹œμ— μ΄λ£¨μ–΄μ§€λ―€λ‘œ Conflicts κ°€ λ°œμƒν•œλ‹€.

Memory Increment


이 문제λ₯Ό ν•΄κ²°ν•˜λŠ” 방법 쀑 ν•œ κ°€μ§€λŠ” In-Out Parameters둜 μ „λ‹¬λ˜λŠ” 원본 데이터가 μž¬μ°Έμ‘°λ˜μ§€ μ•Šλ„λ‘ λͺ…ν™•ν•˜κ²Œ 값을 볡사해 μ „λ‹¬ν•˜κ³ , ν•¨μˆ˜κ°€ μ’…λ£Œλœ ν›„ 원본 값을 μ—…λ°μ΄νŠΈ ν•˜λŠ” 것이닀.

var stepSize = 1

// Make an explicit copy.
var copyOfStepSize = stepSize

func increment(_ number: inout Int) {
    number += stepSize
}

increment(&copyOfStepSize)

// Update the original.
stepSize = copyOfStepSize

print(stepSize) // 2


그리고 In-Out Parametersλ₯Ό 전달할 λ•Œ μΆ”κ°€λ‘œ μ£Όμ˜ν•΄μ•Ό ν•  것은, μ—¬λŸ¬ 개의 Parameters 에 λ™μΌν•œ λ³€μˆ˜λ₯Ό μ „λ‹¬ν•˜λŠ” 것이 κ°€λŠ₯ν•œ 일반 Parameters 와 달리 λ™μΌν•œ λ³€μˆ˜λ₯Ό 전달할 수 μ—†λ‹€λŠ” 것이닀.

  • 일반 Parameters λŠ” λ™μΌν•œ λ³€μˆ˜λ₯Ό 2개의 Parameters 에 전달할 수 μžˆλ‹€.
func balance(_ x: Int, _ y: Int) -> (Int, Int) {
    let sum = x + y
    return (sum / 2, sum - x)
}

var playerOneScore = 42
var playerTwoScore = 30
let (lhs1, rhs1): (Int, Int) = balance(playerOneScore, playerTwoScore)
let (lhs2, rhs2): (Int, Int) = balance(playerOneScore, playerOneScore)

print(lhs1, rhs1) // 36 30
print(lhs2, rhs2) // 42 42
  • In-Out ParametersλŠ” λ™μΌν•œ λ³€μˆ˜λ₯Ό 전달할 수 μ—†λ‹€.
func balance(_ x: inout Int, _ y: inout Int) {
    let sum = x + y
    x = sum / 2
    y = sum - x
}

var playerOneScore = 42
var playerTwoScore = 30
balance(&playerOneScore, &playerTwoScore) // OK
balance(&playerOneScore, &playerOneScore) // error: conflicting accesses to playerOneScore

balance(&playerOneScore, &playerTwoScore)λŠ” 두 개의 Parameters κ°€ λͺ¨λ‘ Overlap λ˜μ§€λ§Œ, λ©”λͺ¨λ¦¬μ˜ λ‹€λ₯Έ μœ„μΉ˜μ— μ ‘κ·Όν•˜λ―€λ‘œ Conflicts κ°€ λ°œμƒν•˜μ§€ μ•ŠλŠ”λ‹€.
반면, balance(&playerOneScore, &playerOneScore)λŠ” 두 개의 Parameters κ°€ λ™μ‹œμ— λ©”λͺ¨λ¦¬μ˜ 같은 μœ„μΉ˜μ— Write Access λ₯Ό μˆ˜ν–‰ν•˜λ―€λ‘œ Conflicts κ°€ λ°œμƒν•œλ‹€.


4. Conflicting Access to self in Methods πŸ‘©β€πŸ’»

Structures 의 mutating methodsλŠ” λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•˜λŠ” λ™μ•ˆ self에 λŒ€ν•œ Write Access λ₯Ό κ°–λŠ”λ‹€.

각 ν”Œλ ˆμ΄μ–΄λŠ” 데미지λ₯Ό μž…μœΌλ©΄ 체λ ₯이 쀄어듀고, 특수 λŠ₯λ ₯을 μ‚¬μš©ν•˜λ©΄ μ—λ„ˆμ§€κ°€ μ€„μ–΄λ“œλŠ” κ²Œμž„μ΄ μžˆλ‹€κ³  μƒκ°ν•΄λ³΄μž.

struct Player {
    var name: String
    var health: Int
    var energy: Int

    static let maxHealth = 10
    mutating func restoreHealth() {
        health = Player.maxHealth
    }
}

restoreHealth() λ©”μ„œλ“œμ˜ self 에 λŒ€ν•œ Write Access λŠ” λ©”μ„œλ“œμ˜ ν˜ΈμΆœμ‹œ μ‹œμž‘λ˜μ–΄ λ°˜ν™˜λ  λ•ŒκΉŒμ§€ μœ μ§€λœλ‹€. 이 λ©”μ„œλ“œλŠ” 내뢀에 Player instance 의 Properties 에 Overlapping Access(쀑볡 μ ‘κ·Ό)ν•˜λŠ” λ‹€λ₯Έ μ½”λ“œλŠ” μ—†λ‹€.

extension Player {
    mutating func shareHealth(with teammate: inout Player) {
        balance(&teammate.health, &health)
    }
}

ν™•μž₯으둜 μΆ”κ°€ν•œ shareHealth(with:) λ©”μ„œλ“œλŠ” In-Out Parameters 둜 λ‹€λ₯Έ Player 의 Instance λ₯Ό 가지고 있으며, Overlapping Access 접근에 λŒ€ν•œ κ°€λŠ₯성을 λ§Œλ“ λ‹€.

var oscar = Player(name: "Oscar", health: 10, energy: 10)
var maria = Player(name: "Maria", health: 5, energy: 10)
oscar.shareHealth(with: &maria) // OK

print(oscar) // Player(name: "Oscar", health: 8, energy: 10)
print(maria) // Player(name: "Maria", health: 7, energy: 10)

Memory Share 1

μœ„ μ½”λ“œμ—μ„œ oscar 의 mutating methods shareHealth(with:)κ°€ κ°–λŠ” Write Access 의 λŒ€μƒμ€ self, 즉, oscar 자기 μžμ‹ μ΄κ³ , In-Out Parameters 둜 μ „λ‹¬λ˜λŠ” maria κ°€ κ°–λŠ” Write Access 의 λŒ€μƒμ€ maria 이기 λ•Œλ¬Έμ— Conflicts κ°€ λ°œμƒν•˜μ§€ μ•ŠλŠ”λ‹€.


κ·ΈλŸ¬λ‚˜ shareHealth(with:)의 In-Out Parameters 둜 oscar λ₯Ό μ „λ‹¬ν•˜λ©΄ mutating methods 의 self와 In-Out Parametersκ°€ λ™μΌν•œ oscar λ₯Ό λŒ€μƒμœΌλ‘œ Write Access λ₯Ό ν•˜κΈ° λ•Œλ¬Έμ— λ™μ‹œμ— 같은 λ©”λͺ¨λ¦¬λ₯Ό μ°Έμ‘°ν•˜κ³  Overlap λ˜λ―€λ‘œ Conflicts κ°€ λ°œμƒν•œλ‹€.

oscar.shareHealth(with: &oscar) // error: inout arguments are not allowed to alias each other

Memory Share 2


5. Conflicting Access to Properties πŸ‘©β€πŸ’»

Structures, Tuples, Enumerations 와 같은 Value TypesλŠ” Structure 의 Properties λ˜λŠ” Tuple 의 Elements와 같은 κ°œλ³„ ꡬ성 κ°’(individual constituent values)으둜 κ΅¬μ„±λœλ‹€. 이것은 Value Types 이기 λ•Œλ¬Έμ— κ°’μ˜ 일뢀가 λ³€κ²½λ˜λ³€ 전체가 λ³€κ²½λœλ‹€.
즉, Properties 쀑 ν•˜λ‚˜μ˜ Read Access λ˜λŠ” Write Access 접근을 ν•˜λŠ” 것은 selfλ₯Ό ν†΅ν•œ 접근이기 λ•Œλ¬Έμ— μ‹€μ œλ‘œ 전체 값에 λŒ€ν•œ Read Access λ˜λŠ” Write Access λ₯Ό μš”κ΅¬ν•˜λŠ” 것과 κ°™λ‹€.

func balance(_ x: inout Int, _ y: inout Int) {
    let sum = x + y
    x = sum / 2
    y = sum - x
}

var playerInformation = (health: 10, energy: 20)
balance(&playerInformation.health, &playerInformation.energy)
// error: conflicting access to properties of playerInformation

μœ„ μ˜ˆμ œμ—μ„œ balance(_:_:)λ₯Ό ν˜ΈμΆœν•˜λŠ” 것은 playerInformation 에 Overlapping Write Accesses λ₯Ό ν•˜λŠ” κ²ƒμ΄λ―€λ‘œ Conflicts κ°€ λ°œμƒν•œλ‹€.

λ§Œμ•½, λ‹€μŒκ³Ό 같이 Tuple 을 μ΄μš©ν•΄ ν•˜λ‚˜μ˜ In-Out Parameter 둜 μ „λ‹¬λ˜λ©΄ Conflicts κ°€ λ°œμƒν•˜μ§€ μ•ŠλŠ”λ‹€.

func balance(_ player: inout (health: Int, energy: Int)) {
    let sum = player.health + player.energy
    player.health = sum / 2
    player.energy = sum - player.health
}

var playerInformation = (health: 10, energy: 20)
balance(&playerInformation)
print(playerInformation)    // (health: 15, energy: 15)


μ•„λž˜ μ½”λ“œλ„ λ§ˆμ°¬κ°€μ§€μ˜ 이유둜 Conflicts κ°€ λ°œμƒν•œλ‹€.

var holly = Player(name: "Holly", health: 10, energy: 20)
balance(&holly.health, &holly.energy)  // Error
print(holly)


이 문제λ₯Ό ν•΄κ²°ν•˜λŠ” 방법 쀑 ν•œ κ°€μ§€λŠ” In-Out Parameters 둜 μ „λ‹¬λ˜λŠ” 원본 데이터λ₯Ό Global Variable 이 μ•„λ‹Œ Local Variable 둜 λ³€κ²½ν•˜λŠ” 것이닀. 그러면 Swift compiler λŠ” Structure 의 Stored Properties 에 λŒ€ν•œ Access κ°€ λ‹€λ₯Έ μ½”λ“œμ˜ λΆ€λΆ„κ³Ό μƒν˜Έμž‘μš©ν•˜μ§€ μ•ŠμœΌλ―€λ‘œ μ•ˆμ „ν•˜λ‹€λŠ” 것을 증λͺ…ν•  수 있게 되고, 2개의 In-Out Parameters κ°€ μ „λ‹¬λ˜μ§€λ§Œ μ •μƒμ μœΌλ‘œ μž‘λ™ν•  수 μžˆλ‹€.

func someFunction() {
    var holly = Player(name: "Holly", health: 10, energy: 20)
    balance(&holly.health, &holly.energy)
    print(holly)
}

someFunction()
Player(name: "Holly", health: 15, energy: 15)

μœ„ μ½”λ“œμ— λŒ€ν•΄ 보좩 μ„€λͺ…을 ν•˜μžλ©΄ λ‹€μŒκ³Ό κ°™λ‹€.

Structure 의 Properties 에 λŒ€ν•œ 쀑볡 μ ‘κ·Ό(Overlapping Access) μ œν•œμ€ λ©”λͺ¨λ¦¬ μ•ˆμ „μ„±μ„ μœ„ν•΄ 항상 ν•„μš”ν•œ 것은 μ•„λ‹ˆλ‹€. λ©”λͺ¨λ¦¬ μ•ˆμ „μ„±μ€ 보μž₯λ˜μ§€λ§Œ, 배타적 μ ‘κ·Ό(exclusive access)은 λ©”λͺ¨λ¦¬ μ•ˆμ „μ„±(memory safety) 보닀 더 μ—„κ²©ν•œ μš”κ΅¬μ‚¬ν•­μ΄λ‹€.

즉, 일뢀 μ½”λ“œλŠ” λ©”λͺ¨λ¦¬μ— λŒ€ν•œ Exclusive Accessλ₯Ό μœ„λ°˜ν•˜λ”λΌλ„ Memory Safetyλ₯Ό μœ μ§€ν•œλ‹€λŠ” 것을 μ˜λ―Έν•œλ‹€. μ΄λŠ” μœ„μ™€ 같이 Swift compiler κ°€ λ©”λͺ¨λ¦¬μ— λŒ€ν•œ 비배타적 μ ‘κ·Ό(nonexclusive access)κ°€ μ—¬μ „νžˆ μ•ˆμ „ν•˜λ‹€λŠ” 것을 증λͺ…ν•  수 μžˆλŠ” Memory Safetyλ₯Ό ν—ˆμš©ν•œλ‹€.

Swift compiler 에 μ˜ν•΄ λ©”λͺ¨λ¦¬μ— λŒ€ν•œ Nonexclusive Accessκ°€ Memory Safetyλ₯Ό κ°€μ§ˆ 수 μžˆλŠ” 쑰건은 λ‹€μŒκ³Ό κ°™λ‹€.

  • 였직 Instance 의 Stored Properties μ—λ§Œ μ ‘κ·Όν•΄μ•Όν•œλ‹€(not Computed Properties or Class Properties).
  • Structure κ°€ Local Variable의 κ°’μ–΄μ•Όν•œλ‹€(not Global Variable).
  • Structure λŠ” μ–΄λ–€ Closures 에도 μΊ‘μ²˜λ˜μ§€ μ•Šκ±°λ‚˜ or Nonescaping Closures 에 μ˜ν•΄μ„œλ§Œ μΊ‘μ²˜λ˜μ–΄μ•Όν•œλ‹€. (일반 Closures λ˜λŠ” Escaping Closures λŠ” ν•¨μˆ˜ context 외뢀와 μƒν˜Έμž‘μš©μ„ ν•˜λ―€λ‘œ μ™„μ „νžˆ 격리 λ˜μ§€ μ•ŠλŠ”λ‹€.)




Reference

  1. β€œMemory Safety.” The Swift Programming Language Swift 5.7. accessed Mar. 13, 2023, Swift Docs Chapter 25 - Memory Safety.