🔥 메서드에서 self에 대한 접근 충돌
구조체의 변경 메서드(mutating method)는 메서드 호출 동안 self
에 대한 쓰기 접근을 가집니다. 예를 들어, 각 플레이어가 체력(health)과 에너지(energy)를 가지고 있는 게임을 생각해 봅시다. 체력은 데미지를 받을 때 감소하고, 에너지는 특수 능력을 사용할 때 감소하죠.
struct Player { var name: String var health: Int var energy: Int static let maxHealth = 10 mutating func restoreHealth() { health = Player.maxHealth } }
swift
위의 restoreHealth()
메서드에서 self
에 대한 쓰기 접근은 메서드가 시작될 때 시작되고 메서드가 반환될 때까지 지속됩니다. 이 경우, restoreHealth()
내부에는 Player
인스턴스의 속성에 대해 겹치는 접근을 할 수 있는 다른 코드가 없어요. 아래의 shareHealth(with:)
메서드는 다른 Player
인스턴스를 in-out 매개변수로 받아, 겹치는 접근의 가능성을 만들어냅니다.
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: &maria) // OK
swift
위의 예제에서, Oscar의 플레이어가 Maria의 플레이어와 체력을 공유하기 위해 shareHealth(with:)
메서드를 호출하는 것은 충돌을 일으키지 않습니다. oscar
는 변경 메서드에서 self
의 값이므로 메서드 호출 동안 oscar
에 대한 쓰기 접근이 있고, maria
는 in-out 매개변수로 전달되었으므로 같은 기간 동안 maria
에 대한 쓰기 접근이 있어요. 아래 그림에서 볼 수 있듯이, 이들은 메모리의 서로 다른 위치에 접근합니다. 두 쓰기 접근이 시간적으로 겹치더라도, 충돌하지는 않아요.
하지만 oscar
를 shareHealth(with:)
의 인자로 전달하면, 충돌이 발생합니다:
oscar.shareHealth(with: &oscar) // 오류: oscar에 대한 접근이 충돌합니다
swift
변경 메서드는 메서드 지속 시간 동안 self
에 대한 쓰기 접근이 필요하고, in-out 매개변수는 같은 기간 동안 teammate
에 대한 쓰기 접근이 필요해요. 메서드 내에서 self
와 teammate
모두 메모리의 같은 위치를 참조하죠. 아래 그림에서 볼 수 있듯이 말이에요. 두 쓰기 접근이 같은 메모리를 참조하고 겹치면서, 충돌이 발생하는 거예요.
이런 상황을 방지하려면, 구조체의 변경 메서드에서는 self
와 겹치는 쓰기 접근을 하는 in-out 매개변수를 사용하지 않는 것이 좋아요. 만약 꼭 필요하다면, 아래와 같이 self
의 속성을 명시적으로 사용하는 것이 좋습니다:
extension Player { mutating func shareHealth(with teammate: inout Player) { balance(&teammate.health, &self.health) } }
swift
이렇게 하면 self
와 teammate
가 메모리의 다른 위치를 참조한다는 것이 명확해지죠.
또 다른 방법은 in-out 매개변수 대신 일반 매개변수를 사용하고 메서드에서 새로운 값을 반환하는 것입니다:
extension Player { func shareHealth(with teammate: Player) -> Player { var balancedTeammate = teammate balance(&balancedTeammate.health, &health) return balancedTeammate } } var oscar = Player(name: "Oscar", health: 10, energy: 10) var maria = Player(name: "Maria", health: 5, energy: 10) maria = oscar.shareHealth(with: maria)
swift
이렇게 하면 self
에 대한 쓰기 접근과 teammate
에 대한 쓰기 접근이 시간적으로 겹치지 않아 충돌을 피할 수 있어요.
구조체의 변경 메서드에서 self
에 대한 쓰기 접근은 강력하지만, 주의해서 사용해야 합니다. self
와 겹치는 쓰기 접근을 하는 in-out 매개변수를 피하고, 가능하다면 일반 매개변수와 반환 값을 사용하는 것이 안전한 코딩을 하는 데 도움이 될 거예요.