Copy-on-Write
Write가 발생했을 때 Copy를 수행한다.
Copy on write는 데이터를 복사할 때 먼저 참조를 통해 불필요한 복사를 줄이고, 데이터 변경이 발생하는 경우에만 실제로 복사를 하는 리소스 관리 기법이다. 이는 원래 운영체제에서 프로세스에게 프레임을 할당할 때 메모리 공간 낭비를 막기 위해 사용되던 기능인데 Swift에도 비슷한 목적으로 적용되었다.
예를 들어 1000개의 요소가 있는 배열이 있고 해당 배열을 다른 변수에 복사해야 하는 경우 두 배열이 같더라도 Swift는 1000개 요소를 모두 복사하여 새로운 메모리 공간에 할당해야 한다. 이처럼 엄청난 양의 같은 데이터를 복사하는 것은 비용이 많이 드는 작업이며 낭비이다.
그래서 사용되는 것이 Copy on write이다.
예제
간단한 코드를 통해 원리를 이해해 보자.
아래와 같이 array1을 선언하고 array2에 array1을 할당한다.
이 경우에는 현재 데이터가 여전히 동일하기 때문에 두 인스턴스의 주소값 또한 동일한 것을 확인할 수 있다.
import Foundation
// 메모리 프린트용 함수 (UnsafeRawPointer : 메모리의 데이터에 접근할 수 있는 프린터)
func print(address o: UnsafeRawPointer ) {
print(String(format: "%p", Int(bitPattern: o)))
}
// array1을 생성하고 array2에 대입 : 메모리 주소 똑같음
var array1: [Int] = [1, 2, 3]
var array2 = array1
print(address: array1) //0x60000373c660
print(address: array2) //0x60000373c660
즉, Copy on write에 의해 복사되지 않고 참조하는 형태가 된다.
array1이나 array2 둘 중 하나를 변경(수정)하게 된다면?
// 값을 변경했을 때 : 메모리 주소 바뀜
array2.append(4)
print(address: array2) //0x6000000aa100
수정이 발생하여 참조를 끊고 array2의 값이 메모리에 할당되는 것을 알 수 있다.
이처럼 실제로 필요할 때까지 복사 작업을 지연함으로써 Swift는 메모리가 낭비되지 않도록 할 수 있다.
Copy-on-Write 직접 구현하기
Swift에서 Copy on write는 Collection 타입 외의 다른 Value type에는 구현되어 있지 않다. 정보가 많은 구조체가 있는 경우 앱의 성능을 개선하고 쓸모없는 복사본을 피하기 위해 Copy on write가 필요한 경우가 발생할 수 있다. 구조체의 경우 구현되어 있지 않기 때문에 직접 구현해줘야 한다.
final class Ref<T> {
var val: T
init(_ v: T) { val = v }
}
struct Box<T> {
var ref: Ref<T>
init(_ x: T) { ref = Ref(x) }
var value: T {
get { return ref.val }
set {
if !isKnownUniquelyReferenced(&ref) { // 유일하게 참조되고 있는지 확인
ref = Ref(newValue)
return
}
ref.val = newValue
}
}
}
struct 내부에 class 인스턴스를 저장 프로퍼티로 소유하고, 그 인스턴스 내부에 프로퍼티로 원본 데이터를 저장한다. 그리고 getter에서는 참조의 데이터를 그대로 반환하지만, setter에서 유일하게 참조되고 있는지를 확인하고 아니라면 새로운 참조 인스턴스를 생성해서 반환하도록 구현한다.
Reference
https://github.com/apple/swift/blob/main/docs/OptimizationTips.rst
https://medium.com/@lucianoalmeida1/understanding-swift-copy-on-write-mechanisms-52ac31d68f2f
'iOS > Swift' 카테고리의 다른 글
[Swift] Class와 Struct의 차이 (0) | 2022.09.29 |
---|---|
[Swift] 옵셔널 패턴(Optional Pattern) (0) | 2022.09.28 |
[Swift] 옵셔널 바인딩(Optional Binding) (0) | 2021.11.24 |
댓글