🔥 In-Out 매개변수에 대한 접근 충돌

317자
4분

함수는 모든 in-out 매개변수에 대해 장기적인 쓰기 접근을 가집니다. in-out 매개변수에 대한 쓰기 접근은 non-in-out 매개변수가 모두 평가된 후에 시작되며, 해당 함수 호출이 지속되는 내내 유지됩니다. 여러 개의 in-out 매개변수가 있다면, 쓰기 접근은 매개변수가 나타나는 순서와 같은 순서로 시작되죠.

이런 장기적인 쓰기 접근의 결과 중 하나는, 스코프 규칙과 접근 제어가 허용하더라도 in-out으로 전달된 원래 변수에 접근할 수 없다는 것입니다. 원본에 대한 어떤 접근도 충돌을 일으키기 때문이에요. 예를 들어 보겠습니다:

var stepSize = 1
 
func increment(_ number: inout Int) {
    number += stepSize
}
 
increment(&stepSize)
// 오류: stepSize에 대한 접근이 충돌합니다
swift

위 코드에서 stepSize는 전역 변수이며, 보통은 increment(_:) 내부에서 접근 가능합니다. 하지만 stepSize에 대한 읽기 접근이 number에 대한 쓰기 접근과 겹치고 있어요. 아래 그림에서 볼 수 있듯이, numberstepSize 둘 다 메모리의 같은 위치를 참조하고 있습니다. 읽기 접근과 쓰기 접근이 같은 메모리를 참조하고 겹치면서 충돌이 발생하는 거죠.

lecture image

이 충돌을 해결하는 한 가지 방법은 stepSize의 명시적인 복사본을 만드는 것입니다:

// 명시적인 복사본을 만듭니다.
var copyOfStepSize = stepSize
increment(&copyOfStepSize)
 
// 원본을 업데이트합니다.
stepSize = copyOfStepSize
// stepSize는 이제 2입니다
swift

increment(_:)를 호출하기 전에 stepSize의 복사본을 만들면, copyOfStepSize의 값이 현재 단계 크기만큼 증가한다는 것이 명확해집니다. 읽기 접근이 쓰기 접근 전에 끝나므로 충돌이 없어요.

in-out 매개변수에 대한 장기적 쓰기 접근의 또 다른 결과는 하나의 변수를 같은 함수의 여러 in-out 매개변수에 인자로 전달하면 충돌이 발생한다는 것입니다. 예를 들면:

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)
// 오류: playerOneScore에 대한 접근이 충돌합니다
swift

위의 balance(_:_:) 함수는 두 매개변수를 수정하여 전체 값을 균등하게 나눕니다. playerOneScoreplayerTwoScore를 인자로 호출하면 충돌이 발생하지 않아요. 시간적으로 겹치는 두 개의 쓰기 접근이 있지만, 서로 다른 메모리 위치에 접근하기 때문입니다. 반면에 playerOneScore를 두 매개변수의 값으로 전달하면 충돌이 발생하는데, 이는 같은 시간에 같은 메모리 위치에 두 번의 쓰기 접근을 시도하기 때문이죠.