🔥 In-Out 매개변수에 대한 접근 충돌
함수는 모든 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
에 대한 쓰기 접근과 겹치고 있어요. 아래 그림에서 볼 수 있듯이, number
와 stepSize
둘 다 메모리의 같은 위치를 참조하고 있습니다. 읽기 접근과 쓰기 접근이 같은 메모리를 참조하고 겹치면서 충돌이 발생하는 거죠.
이 충돌을 해결하는 한 가지 방법은 stepSize
의 명시적인 복사본을 만드는 것입니다:
// 명시적인 복사본을 만듭니다. var copyOfStepSize = stepSize increment(©OfStepSize) // 원본을 업데이트합니다. 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(_:_:)
함수는 두 매개변수를 수정하여 전체 값을 균등하게 나눕니다. playerOneScore
와 playerTwoScore
를 인자로 호출하면 충돌이 발생하지 않아요. 시간적으로 겹치는 두 개의 쓰기 접근이 있지만, 서로 다른 메모리 위치에 접근하기 때문입니다. 반면에 playerOneScore
를 두 매개변수의 값으로 전달하면 충돌이 발생하는데, 이는 같은 시간에 같은 메모리 위치에 두 번의 쓰기 접근을 시도하기 때문이죠.