드디어 mutating
에 대해서 다룬다!
Swift struct
, class
에 대해 공부할때 mutating
에 대해 많이 고민했다...
도대체 이게 뭔지 왜 이걸 붙여야 하는지!
이제는 정리할 때인거 같아서 포스팅한다.
오늘도 공식 문서를 먼저 봐보자.
struct
와 enum
은 value type이다.
기본적으로 value type의 property들은 인스턴스 메서드에 의해 수정될 수 없다.
하지만, 특정 메서드 내에서 struct
, enum
의 property를 수정해야하는 경우 mutating
을 통해 해결할 수 있다.
mutating
을 선언한 메서드는 메서드 내에서 property를 변경할 수 있고, 메서드가 종료될 때 변경한 모든 내용을 원래 struct
에 다시 기록한다.
메서드는 self property에 새 인스턴스를 할당할 수도 있다.
struct Point {
var x = 0.0, y = 0.0
mutating func moveBy(x deltaX: Double, y deltaY: Double) {
x += deltaX
y += deltaY
}
}
var somePoint = Point(x: 1.0, y: 1.0)
somePoint.moveBy(x: 2.0, y: 3.0)
print("The point is now at (\(somePoint.x), \(somePoint.y))")
Point
struct에 Point
인스턴스를 이동하는 mutating func moveBy()
를 정의했다.
새로운 Point를 반환하는 대신 실제로 호출 된 Point를 수정한다.
만약, let
으로 선언된 인스턴스는 property가 변수여도 속성을 변경할 수 없기 때문에 mutating
메서드를 호출할 수 없다.
흠.. 정리를 해보자
struct
, enum
은 value type이다.mutating
은 이를 가능하게 해준다.그렇다면 메서드가 종료될 때 변경한 모든 내용을 원래 struct
에 다시 기록은 무슨 말일까?
Swift Array Code를 살펴보면 아래와 같은 설명을 볼 수 있다.
Arrays, like all variable-size collections in the standard library, use copy-on-write optimization.
Multiple copies of an array share the same storage until you modify one of the copies.
When that happens, the array being modified replaces its storage with a uniquely owned copy of itself, which is then modified in place.
Optimizations are sometimes applied that can reduce the amount of copying.
This means that if an array is sharing storage with other copies, the first mutating operation on that array incurs the cost of copying the array.
An array that is the sole owner of its storage can perform mutating operations in place.
In the example below, anumbers
array is created along with two copies that share the same storage.
When the originalnumbers
array is modified, it makes a unique copy of its storage before making the modification.
Further modifications tonumbers
are made in place, while the two copies continue to share the original storage.
표준 라이브러리의 모든 가변 크기 collection과 마찬가지로 배열은 copy-on-write(COW) 최적화를 사용한다.
Array의 여러 복사본은 복사본 중 하나를 수정할 때까지 동일한 저장소를 공유한다.
수정되는 Array는 원본의 복사본으로 교체한 다음 제자리에서 수정된다.
복사량을 줄이기 위해 최적화가 적용되는 경우가 있다.
즉, Array가 다른 복사본과 저장소를 공유하는 경우 해당 Array의 첫번째 mutating
으로 Array 복사가 발생한다.
var numbers = [1, 2, 3, 4, 5]
var firstCopy = numbers
var secondCopy = numbers
// The storage for 'numbers' is copied here
numbers[0] = 100
numbers[1] = 200
numbers[2] = 300
// 'numbers' is [100, 200, 300, 4, 5]
// 'firstCopy' and 'secondCopy' are [1, 2, 3, 4, 5]
위의 경우에서도 numbers, firstCopy, secondCopy가 처음에는 같은 저장소를 공유하고 있다.
numbers의 값이 바뀔 때, 저장소가 복사되고 numbers는 복사된 저장소를 가지고 있게 된다.
위에서 살펴본 것처럼 Swift는 값이 변경될 때 복사를 하는 COW로 최적화를 진행한다.
struct
의 property 값이 변경될 때에는 Swift가 직관적으로 알 수 있다.
(이는 let
, var
키워드를 통해 컴파일러가 이미 알고 있다.)
mutating
키워드 없이 메서드가 property를 변경하게 되면 언제 실제로 복사를 해야하는지 알 수가 없다.
따라서, mutating
키워드는 해당 메서드가 호출된다면 실제 복사를 해야한다고 알려주는 역할이다.
드디어 mutating
에 대해서 정리했다.
처음에는 COW에 대해서 모르고 접근해서 이해하기 힘들었는데 알고 보니 넘나리 잘 이해된다.
그럼 이만👋