관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - swift 공식 문서] 25. Memory Safety (메모리 안전, Simultaneous accesses to ... but modification requires exclusive access) 본문

swift 공식 문서

[iOS - swift 공식 문서] 25. Memory Safety (메모리 안전, Simultaneous accesses to ... but modification requires exclusive access)

jake-kim 2021. 7. 27. 11:25

Memory Safety (메모리 안전)

  • atomic: Swift는 메모리의 위치를 수정하는 코드가 해당 메모리에 독점적으로 액세스할 수 있도록 요구하여 동일한 메모리 영역에 다중 액세스가 충돌하지 않는 성질

메모리 Access conflicting

  • 읽기 access, 쓰기 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)!")
  • 메모리 access conflicting: 코드의 다른 부분이 동시에 메모리의 동일한 위치에 access하는 경우 발생
    • 시간에 따라 같은 input을 주어도 다른 output이 나오는 상황
  • atomic인 경우: 즉각적인(instantaneous) 코드를 사용하는 경우
func oneMore(than number: Int) -> Int {
    return number + 1
}

var myNumber = 1
myNumber = oneMore(than: myNumber)
print(myNumber)
// Prints "2"
  • access conflicting가 나는 경우: long-term accesses - 시작된 후 종료되기 전에 다른 코드가 실행되는 경우 (= overlap)
    • 주로 struct에서 inout 매서드를 사용하는 경우 발생

Inout 매개변수에 대한 access conflicting

  • 읽기나 쓰기 중 두 개 이상이 동시에 동일한 메모리 접근

  • stepSize에 대한 읽기와 쓰기가 동시에 일어나는 상황
    • 읽기: 전역변수인 stepSize를 함수 내부에서 stepSize로 읽는 형태
    • 쓰기: 매개변수로 들어온 stepSize가 inout에 의하여 write되는 상태

conflicting

  • 해결방법: 명시적 복사
// Make an explicit copy.
var copyOfStepSize = stepSize
increment(&copyOfStepSize)

// Update the original.
stepSize = copyOfStepSize
  • 여러개의 inout 매개변수를 사용하는 경우 conflicting
    • playerOneScore라는 변수에 동시에 두 개의 쓰기 access 수행
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

메서드에서 self에 대한 Conflicting

  • 동시에 같은 객체에 접근
  • 전역함수 balance 접근
struct Player {
    var name: String
    var health: Int
    var energy: Int

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

    func balance(_ x: inout Int, _ y: inout Int) {
        let sum = x + y
        x = sum / 2
        y = sum - x
    }
    
extension Player {
    mutating func shareHealth(with teammate: inout Player) {
        balance(&teammate.health, &health)
    }
}

var oscar = Player(name: "Oscar", health: 10, energy: 10)
var maria = Player(name: "Maria", health: 5, energy: 10)

oscar.shareHealth(with: &oscar) // Error: conflicting accesses to oscar

동시에 같은 메모리 접근

Property에 대한 충돌

  • referece type은 큰 상관이 없지만, value type인 struct, tuple, enum과 같은 타입은 property 중 하나에 읽기 또는 쓰기 액세스에서 전체 값에 대한 읽기 또는 쓰기가 발생하면서 충돌
var playerInformation = (health: 10, energy: 20)
balance(&playerInformation.health, &playerInformation.energy)
// Error: conflicting access to properties of playerInformation
  • 해결 방법: function과 같은 곳에 지역 변수로 playerInformation을 선언 후 변경
    • 전역변수일때 문제가 되고 지역변수일때는 안전을 보장
var holly = Player(name: "Holly", health: 10, energy: 10)
balance(&holly.health, &holly.energy)  // Error

func someFunction() {
    var oscar = Player(name: "Oscar", health: 10, energy: 10)
    balance(&oscar.health, &oscar.energy)  // OK
}

property에서 conflicting을 보장하는 3가지 원칙

  • computed property나 class property이 아닌 stored property에만 접근
  • struct를 전역 변수로 사용하지 않고 지역 변수의 값으로만 사용
  • strcut는 클로저에 의해 캡처되지 않거나 escaping되지 않는 클로저에만 캡쳐되게끔 사용

* 참고

https://docs.swift.org/swift-book/LanguageGuide/MemorySafety.html

Comments