Swift는 코드에서 발생할 수 있는 안전하지 않은 동작을 방지한다.
Swift는 동일한 메모리 영역에 대한 다중 접근이 일어나지 않도록 막기 위해, 메모리의 위치를 수정하는 코드가 독점 접근 권한을 요구한다. Swift는 메모리를 자동으로 관리하므로 대부분 메모리 접근에 대해 생각할 필요가 없다. 그러나 잠재적 충돌에 대해 이해해야 발생할 수 있는 충돌을 피할 수 있다.
코드의 다른 부분이 같은 시간에 메모리의 같은 위치에 접근할 때, 메모리의 충돌이 발생할 수 있다.
고려해야 할 3가지 특성: 접근하는 동안 읽기 또는 쓰기 접근인지 여부와 메모리의 위치
아래의 조건을 만족하는 2개의 접근이 있을 때 충돌 발생:
쓰기 접근은 메모리의 위치를 변경하지만, 읽기 접근은 변경하지 않는다.
var stepSize = 1 // 전역 변수
func increment(_ number: inout Int) {
number += stepSize
}
increment(&stepSize)
// Error: conflicting accesses to stepSize
// stepSize에 읽기 접근과 number에 쓰기 접근 오버랩됨
// 같은 메모리를 참조하고 오버랩되어 충돌 발생
// 충돌 해결 - stepSize의 복사본을 명확하게 지정
// Make an explicit copy.
var copyOfStepSize = stepSize
increment(©OfStepSize)
// Update the original.
stepSize = copyOfStepSize
// stepSize is now 2
// 읽기 접근은 쓰기 접근이 시작되기 전에 끝나므로 충돌이 일어나지 않음
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
에 대한 쓰기 접근을 가짐.
struct Player {
var name: String
var health: Int
var energy: Int
static let maxHealth = 10
mutating func restoreHealth() {
health = Player.maxHealth
// self에 쓰기 접근은 메소드 반환될 때까지 유지
}
}
extension Player {
// shareHealth(with:) 메소드는 in-out 파라미터로 다른 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. 중복 야기 X
// oscar는 변경 함수에서 self의 값이기 때문에 oscar에 대한 쓰기 접근
// maria는 in-out 파라미터로 전달되기 때문에 maria에 대한 쓰기 접근
// 그러나 shareHealth(with:) 의 인자로 oscar를 전달하면 충돌 발생
oscar.shareHealth(with: &oscar)
// Error: conflicting accesses to oscar
// 변경 메소드는 self에 대한 쓰기 접근이 필요, in-out 파라미터는 teammate에 쓰기 접근 필요
// 메소드 내에서 self와 teammate는 메모리의 같은 위치를 참조, 쓰기 접근 오버랩되므로 충돌
var playerInformation = (health: 10, energy: 20)
balance(&playerInformation.health, &playerInformation.energy)
// Error: conflicting access to properties of playerInformation
// playerInformation에 중복 쓰기 접근이므로 충돌
// playerInformation.health, playerInformation.energy는 쓰기 접근이 필요한 in-out 파라미터
// 전역 변수에 저장된 구조체의 프로퍼티에 쓰기 접근 중복될 때 에러
var holly = Player(name: "Holly", health: 10, energy: 10)
balance(&holly.health, &holly.energy) // Error
// holly 변수가 전역 변수가 아닌 지역 변수로 변경되면 구조체의 저장된 프로퍼티 중복 접근 안전
func someFunction() {
var oscar = Player(name: "Oscar", health: 10, energy: 10)
balance(&oscar.health, &oscar.energy) // OK
}
// 위에서 oscar의 health, energy는 2개의 in-out 파라미터로 전달
// 2개의 저장된 프로퍼티는 상호작용하지 않으므로 안전