Swift. Copy-On-Write

Array

var array = [1, 2, 3, 4, 5]
// 0x6000038a0200
var filteredArray = array.filter { $0 > 3 }
// 0x6000015b3380
var array = [1, 2, 3, 4, 5]
// 0x6000034393d0
var array2 = array as [Any]
// 0x600002234ce0
var array = [1, 2, 3, 4, 5]
// 0x600001127860
var array2 = Array(arrayLiteral: array)
// 0x600003c3f6b0
var array = [1, 2, 3, 4, 5]
// 0x600002e30160
var array2 = array
// 0x600002e30160
array2.append(6)
// 0x600002636320

ArraySlice

final class Element {
let id: Int

init(id: Int) {
self.id = id
}

deinit {
print("deinit of '\(id)' is called")
}
}
var array: Array<Element>? = [Element(id: 1), Element(id: 2)]
// array is [Element(1), Element(2)]
var slice: ArraySlice<Element>? = array!.dropFirst()
// slice is [Element(2)]
slice![slice!.endIndex - 1] = Element(id: 3)
// slice is [Element(3)]
// array is [Element(1), Element(2)]
array = nil
// deinit of '1' is called
// deinit of '2' is called
var array2: Array<Element>? = [Element(id: 10), Element(id: 20)]
// array2 is [Element(10), Element(20)]
var slice2: ArraySlice<Element>? = array2!.dropFirst()
// slice2 is [Element(20)]
slice2!.append(Element(id: 30))
// slice2 is [Element(20), Element(30)]
// array2 is [Element(10), Element(20)]
array2 = nil
// deinit of '10' is called
var array3: Array<Element>? = [Element(id: 100), Element(id: 200)]
// array3 is [Element(100), Element(200)]
var slice3: ArraySlice<Element>? = array3!.dropFirst()
// slice3 is [Element(200)]
array3![array3!.count - 1] = Element(id: 300)
// array3 is [Element(100), Element(300)]
// slice3 is [Element(200)]
array3 = nil
// deinit of '300' is called
var array4: Array<Element>? = [Element(id: 1000), Element(id: 2000)]
// array4 is [Element(1000), Element(2000)]
var slice4: ArraySlice<Element>? = array4!.dropFirst()
// slice4 is [Element(2000)]
array4!.append(Element(id: 3000))
// array4 is [Element(1000), Element(2000), Element(3000)]
// slice4 is [Element(2000)]
array4 = nil
// deinit of '3000' is called

Set

var set: Set = [1, 2]
// set is [1, 2]
var set2 = set
// set2 is [1, 2]
set2.insert(3)
// set2 is [1, 2, 3], set is [1, 2]

Dictionary

var dictionary = [1: "One", 2: "Two", 3: "Three"]
var dictionary2 = dictionary
dictionary2[1] = "1"
// dictionary = [3: "Three", 1: "One", 2: "Two"]
// dictionary2 = [3" "Three", 1: "1", 2: "Two"]

Manual Copy-On-Write

struct User {
var id: Int
var age: Int
}
final class ReferenceTo<T> {
var value: T
init(value: T) {
self.value = value
}
}
struct Box<T> {
private var reference: ReferenceTo<T>

init(value: T) {
reference = ReferenceTo(value: value)
}
var value: T {
get { return reference.value }
set {
// returns true if there is only one strong reference
if isKnownUniquelyReferenced(&reference) {
reference.value = newValue
}
reference = ReferenceTo(value: newValue)
}
}
}
var boxedUser = Box<User>(value: User(id: 1, age: 18))
// boxedUser2.value refers to boxedUser.value
var boxedUser2 = boxedUser
boxedUser2.value.id = 2 // copying on write
// boxedUser2.value.id is 2, boxedUser.value.id is 1

isKnownUniquelyReferenced

  • true == strong
  • false == strong, strong
  • false == weak, unowned
  • true == strong, weak, unowned

Resources

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store