Реализуйте copy-on-write вручную для типа с буфером-class, используя isKnownUniquelyReferenced
Постройте значимый тип Box<T>, хранилище которого — буфер-class, чтобы копии оставались разделёнными до мутации.
Требования:
- Присваивание должно быть O(1) — разделяйте буфер, не копируйте полезную нагрузку.
- Мутация должна копировать буфер, только когда он не удерживается уникально.
- Для решения используйте
isKnownUniquelyReferenced.
final class Buffer<T> { var value: T; init(_ v: T) { value = v } }
struct Box<T> {
private var buffer: Buffer<T>
init(_ v: T) { buffer = Buffer(v) }
var value: T {
get { buffer.value }
set {
// ваш код здесь: скопируйте буфер, если он разделён, затем пишите
}
}
}
Напишите реализацию сеттера.
Оберните буфер-class из кучи в struct. Перед каждой мутацией проверяйте isKnownUniquelyReferenced(&buffer); если false, замените его свежей глубокой копией, затем пишите. Копия возникает лишь при мутации разделённого буфера — ленивая значимая семантика.
- ✗Копируют буфер при каждом присваивании, а не только при мутации разделённого
- ✗Проверяют уникальность в геттере, а не перед записью
- ✗Думают, что
isKnownUniquelyReferencedработает на значимых типах илиweak-ссылках
- →Почему проверку уникальности нужно делать до записи, а не после?
- →Что ломается, если два потока мутируют один общий буфер одновременно?
В сеттере перед записью проверьте уникальность буфера. Если он разделён, замените его свежей копией, иначе пишите на месте:
var value: T {
get { buffer.value }
set {
if !isKnownUniquelyReferenced(&buffer) {
buffer = Buffer(buffer.value) // отделяем разделённый буфер
}
buffer.value = newValue
}
}
Присваивание Box копирует только ссылку на buffer — это O(1). Первая мутация разделённого буфера даёт isKnownUniquelyReferenced == false и делает копию; после этого владелец единственный, и запись идёт на месте. Так значимая семантика сохраняется без копий при чтении.