Как реализовать boxing и проверку ссылок в Swift
Что проверяют
Понимаете ли вы разницу между value- и reference-семантикой и когда её приходится подменять. Под «проверкой ссылок» почти всегда имеют в виду isKnownUniquelyReferenced, так что следом просят написать copy-on-write для своей структуры.
Как отвечать
Сразу оговоритесь: в Swift нет примитивов и автоупаковки, как в Java, Int это обычная структура. Boxing здесь означает ручное оборачивание значения в класс, и нужно оно в трёх случаях:
- общее изменяемое состояние:
final class Box<T> { var value: T }позволяет нескольким владельцам видеть одно значение; - слабые ссылки в коллекциях: массив держит элементы сильно, поэтому для списка наблюдателей используют
struct Weak<T: AnyObject> { weak var value: T? }; - copy-on-write: структура хранит данные в приватном классе и копирует их только при записи, если на хранилище смотрит кто-то ещё.
Проверка в третьем случае такая: в мутирующем сеттере вызываете isKnownUniquelyReferenced(&storage). Если вернулось false, создаёте новое хранилище с копией и меняете уже его. Функция считает только сильные ссылки, weak и unowned не в счёт. Так устроены Array, Dictionary и String: присваивание дешёвое, платите вы только при первой записи в копию.
Упомяните и неявную упаковку, которую делает компилятор. Экзистенциал any Protocol держит значение во встроенном буфере на три машинных слова, а если значение больше, выносит его в кучу. indirect enum тоже хранит рекурсивные случаи в куче.
Для сравнения ссылок есть оператор ===: он проверяет, что это один и тот же экземпляр, а == сравнивает значения через Equatable.
Где спотыкаются
Путают boxing с ARC и перечисляют strong, weak, unowned, не сказав ни слова про упаковку и copy-on-write.
Вызывают isKnownUniquelyReferenced вне мутирующего метода или на копии ссылки, а не на самом хранимом свойстве. Кроме того, функция не потокобезопасна: при доступе из нескольких потоков нужна своя синхронизация.
Ставят unowned там, где объект может исчезнуть раньше. Обращение к такой ссылке после освобождения роняет приложение, а weak в той же ситуации просто становится nil.