Swift의 mutating은 struct와 enum의 instance method가 property 또는 self 자체를 변경할 수 있다고 선언하는 keyword다. ‘struct의 모든 property는 기본적으로 immutable’해서 붙이는 것이 아니다. instance가 var인지 let인지, property가 var인지, method가 mutating인지 세 조건을 나눠 봐야 한다.
struct property를 바꾸는 method
Point의 좌표를 현재 instance 안에서 변경하려면 method 앞에 mutating을 붙인다.
struct Point {
var x: Int
var y: Int
mutating func moveBy(x deltaX: Int, y deltaY: Int) {
x += deltaX
y += deltaY
}
}
var point = Point(x: 0, y: 0)
point.moveBy(x: 5, y: 5)
print(point) // Point(x: 5, y: 5)
x와 y가 var여야 하고, moveBy가 mutating이어야 하며, 호출하는 point도 var binding이어야 한다. 다음처럼 instance를 let으로 만들면 stored property가 var여도 mutating method를 호출할 수 없다.
let fixedPoint = Point(x: 0, y: 0)
// fixedPoint.moveBy(x: 1, y: 1) // compile error
반대로 값을 읽기만 하는 method에는 mutating이 필요 없다.
extension Point {
func distanceSquaredFromOrigin() -> Int {
x * x + y * y
}
}
property뿐 아니라 self 전체를 교체할 수 있다
mutating method는 개별 property를 바꾸는 것뿐 아니라 같은 type의 새 값으로 self를 대입할 수 있다.
extension Point {
mutating func moveToOrigin() {
self = Point(x: 0, y: 0)
}
}
enum의 상태 전환도 같은 원리다. enum에는 바꿀 stored property가 없어도 현재 case인 self를 다른 case로 교체한다.
enum LightSwitch {
case off
case on
mutating func toggle() {
switch self {
case .off:
self = .on
case .on:
self = .off
}
}
}
var light = LightSwitch.off
light.toggle()
print(light) // on
class와 다른 점
class는 reference type이므로 instance method에 mutating keyword를 쓰지 않는다. let으로 선언한 class reference도 reference가 다른 object를 가리키도록 재대입할 수 없을 뿐, object의 var property는 method를 통해 바뀔 수 있다.
final class Counter {
var value = 0
func increment() {
value += 1
}
}
let counter = Counter()
counter.increment() // 가능
이 차이를 ‘class는 mutable, struct는 immutable’이라는 한 문장으로 줄이면 오해가 생긴다. struct도 var instance에서 mutating method로 바꿀 수 있고, class property도 let이면 대입할 수 없다. 핵심은 value 전체의 변경과 reference가 가리키는 object 상태 변경의 차이다.
value type을 복사한 뒤 한쪽에서 mutating method를 호출하면 다른 복사본의 값은 그대로인 것이 기본 의미다. 다만 내부에 reference type property가 있거나 standard collection의 copy-on-write 구현이 개입하면 memory 동작은 더 세밀하게 봐야 한다.
protocol requirement에서의 mutating
struct와 enum도 상태 변경 contract를 구현할 수 있게 하려면 protocol requirement에 mutating을 적는다.
protocol Togglable {
mutating func toggle()
}
extension LightSwitch: Togglable {}
class가 이 protocol을 구현할 때 구현 method에는 mutating을 쓰지 않는다. keyword는 value type implementation이 instance를 바꿀 수 있도록 protocol contract에 여지를 주는 역할이다.
선택 기준
- method가 value type의 stored property나
self를 바꾸면mutating을 사용한다. - 새 값을 반환하는 방식이 더 이해하기 쉬운지 함께 비교한다.
- 호출할 instance가
let이면 mutating method를 호출할 수 없음을 확인한다. - protocol이 class와 value type 모두의 상태 변경을 허용해야 하면 requirement에
mutating을 붙인다.
class와 struct의 선택 기준은 SwiftUI에서 class와 struct 구분, closure가 value와 reference를 capture하는 방식은 Swift closure와 callback에서 연결된다.
참고 자료
'배움과 성장 > 소프트웨어 개발' 카테고리의 다른 글
| Xcode 단축키 핵심 정리: 탐색·편집·빌드·디버깅 (0) | 2024.11.13 |
|---|---|
| SwiftUI View 구조: var body·struct·상태 객체의 역할 (0) | 2024.11.11 |
| SwiftUI에서 struct와 class 고르기: @State·@Observable 기준 (1) | 2024.11.11 |
| Swift ARC의 strong·weak·unowned: 수명 관계로 참조 고르기 (2) | 2024.11.10 |
| Swift closure와 callback: capture·escaping·호출 시점 구분하기 (4) | 2024.11.09 |
댓글