Как проверить на Copy-on-Write
1. У Array адрес данных видно через withUnsafeBufferPointer. Копируем, смотрим адрес, меняем копию, смотрим снова.
var array = [1, 2, 3]
var arrayCopy = array
isSameAddress(array, arrayCopy) // true, смотрят на одни данные
arrayCopy[0] = 10
isSameAddress(array, arrayCopy) // false, у arrayCopy своя копия
print(array)
print(arrayCopy)
func isSameAddress(_ array: [Int], _ arrayCopy: [Int]) {
// withUnsafeBufferPointer - указатель на элементы массива в памяти
array.withUnsafeBufferPointer { arrayPointer in
// У указателя есть:
// baseAddress - адрес первого элемента
// count - количество элементов
arrayCopy.withUnsafeBufferPointer { arrayCopyPointer in
// Сравниваем указатели на первые элементы
print(arrayPointer.baseAddress == arrayCopyPointer.baseAddress)
}
}
}2. Для двух копий ContiguousArray проверка та же.
3. Для ArraySlice сравнивают две копии слайса. С самим массивом адрес совпадёт, только если слайс начинается с индекса 0.
4. У AttributedString открытого указателя нет, в отличие от массива. Но смысл тот же: копируем, меняем одну переменную, вторая не должна измениться.
5. У Set и Dictionary такого указателя нет. Но тут мы можем взглянуть в исходники:
internal struct _NativeSet<Element: Hashable> {
internal var _storage: __RawSetStorage // class в куче
}
internal mutating func ensureUnique(isUnique: Bool, capacity: Int) -> Bool {
if _fastPath(capacity <= self.capacity && isUnique) {
return false
}
if isUnique {
resize(capacity: capacity)
return true
}
if capacity <= self.capacity {
copy() // isUnique == false и capacity хватает: своя копия тех же данных
return false
}
copyAndResize(capacity: capacity) // если capacity не хватает: тоже копируем, но после resize
return true
}
internal mutating func copy() {
let newStorage = _SetStorage.copy(original: _storage) // новый class в куче
// ... копируем элементы ...
_storage = result._storage // этот Set смотрит уже на новый объект
}Ссылка на class в куче и copy() при isUnique == false как раз и есть copy-on-write.
6. У Data то же в Foundation. Перед изменением проверяют class в куче через isKnownUniquelyReferenced. Если ссылка не одна, делают mutableCopy.
mutating func ensureUniqueReference() {
if !isKnownUniquelyReferenced(&_storage) {
_storage = _storage.mutableCopy(_slice)
}
}