Swift Language
Swift Language - 23 - 메모리 안정성 (Memory Safety)
xnoag
2023. 2. 6. 17:23
기본적으로 Swift는 코드가 비정상 적으로 동작하는 것을 막는 행위를 합니다. 예를 들면, 변수가 초기화 되기 전에 사용된다던가, 메모리에서 해제된 값을 접근하는 것을 막는 다던가, 인덱스의 한계를 넘는지 확인한다는 것 들이 그것 입니다. Swift에서는 또 메모리의 같은 영역을 동시에 접근해서 충돌이 나지 않도록 해줍니다. 이렇듯 Swift에서 메모리 관련해 자동으로 관리해 주기때문에 대부분의 경우에는 Swift언어를 사용하는 사용자는 메모리의 접근에 대해 전혀 생각하지 않고 사용해도 됩니다. 하지만 메모리 접근 충돌이 발생할 수 있는 잠재적인 상황을 이애하고 메모리 접근 충돌을 피하는 코드를 어떻게 작성할 수 있는지 이해하는 것은 중요합니다. 만약 메모리 접근 충돌이 일어나면 런타임 에러나, 컴파일 에러가 발생합니다.
메모리 접근 충돌의 이해 (Understanding Conflicting Access to Memory)
// 코드에서 메모리 접근은 아래 예와 같이 변수에 값을 할당하거나 접근할 때 발생합니다.
// A write access to the memory where one is stored.
var one = 1
// A read access from the memory where one is stored.
print("We're number \(one)!")
위 코드는 one이라는 변수에 1을 할당하고 print하기 위해 one을 접근해 사용합니다. 메모리 충돌은 메모리에 값을 할당하고 메모리 값을 접근하는 것을 동시에 수행할때 발생합니다. 예를 들어보겠습니다. 만약 아래와 같이 특정 물건을 구매하고 구매 총 금액을 확인하는 경우 Before의 기본 상태에서 Total은 5$ 입니다. 만약 TV와 T-shirt 를 구매하는 동안(During) Total에 접근해 값을 가져 왔다면 Total은 5$가 됩니다. 하지만 실제 제대로 된 값을 After의 320$ 이어야 할 것 입니다.
Characteristics of Memory Access
메모리 접근이 충돌 할 수 있는 상황의 성격은 3가지 경우가 있습니다. 메모리를 읽거나 쓰는 경우, 접근 지속시간 그리고 메모리가 접근되는 위치입니다. 구체적으로 메모리 충돌은 다음 3가지 조건 중 2가지를 만족하면 발생합니다.
func oneMore(than number: Int) -> Int {
return number + 1
}
var myNumber = 1
myNumber = oneMore(than: myNumber)
print(myNumber)
// Prints "2"
// 위 코드에서는 메모리 접근의 충돌이 발생하지 않습니다.
Conflicting Access to In-Out Parameters
// 메모리 충돌은 in-out 파라미터를 잘못 사용할 때 발생할 수 있습니다. 아래 예제 코드를 보시겠습니다.
var stepSize = 1
func increment(_ number: inout Int) {
number += stepSize
}
increment(&stepSize)
// Error: conflicting accesses to stepSize
// stepSize를 복사한 copyOfStepSize를 사용하면 하나의 메모리를 읽고 쓰는 행위를 동시에 하지 않게돼 접근 충돌을 피할 수 있습니다.
// 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
Conflicting Access to self in Methods
// 메소드 안에서 self에 접근할때 충돌이 발생할 수도 있습니다.
// 예를 들기 위해 아래와 같이 Player구조체를 선언합니다.
struct Player {
var name: String
var health: Int
var energy: Int
static let maxHealth = 10
mutating func restoreHealth() {
health = Player.maxHealth
}
}
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