Swift는 기본적으로 코드 상의 안전하지 않은 행위를 막아줍니다. 예를 들면 variables이 initialize되기 이전에 사용되는 것을 막고, 메모리가 해제(deallocated)된 이후에는 접근하지 않으며, array의 index가 범위를 벗어나지 않는지 체크해줍니다.
Swift는 같은 메모리 공간을 동시 접근해서 충돌이 발생하는 것을 막아줍니다. Swift가 메모리 관리를 자동으로 해주기 때문에 대부분의 경우 memory에 접근하는 것을 고려할 필요가 없지만, 잠재적으로 memory 충돌이 발생할 수 있는 상황을 이해하는 것은 중요합니다.
동시 접근(Overlapping accesses)은 주로 inout
parameter를 사용한 function 또는 구조체의 methods or mutating methods 사용에서 발생합니다.
For example:
var stepSize = 1
func increment(_ number: inout Int) { // write access
number += stepSize // read access
}
increment(&stepSize)
// Error: conflicting accesses to stepSize
number
는 inout
parameter이므로 function 호출 시 write access로 메모리를 참조합니다.
function 내부에서 stepSize
가 read access 하기 위해 메모리에 접근하나, write access와 같은 메모리 위치를 참조하면서(overlap) 메모리 충돌이 발생합니다(conflict).
A mutating method on a structure has write access to self for the duration of the method call. For example, consider a game where each player has a health amount, which decreases when taking damage, and an energy amount, which decreases when using special abilities.
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: &maria) // OK
위 예제는 memory conflict를 발생시키지 않는데, balance
함수가 호출되면서 2개의 inout
parameter가 write access로 메모리를 참조하지만 서로 다른 위치의 메모리에 접근하므로 충돌이 발생하지 않습니다.
하지만, oscar
를 함수의 인자로 전달하면 conflict가 발생합니다.
oscar.shareHealth(with: &oscar)
// Error: conflicting accesses to oscar
mutating method는 self
의 write access가 필요한데, inout
parameter로 self
인 oscar
를 전달하게 되면 같은 메모리 위치를 참조하게 되므로, 두 write access는 충돌하게 됩니다.