Какой контракт у withContiguousStorageIfAvailable для произвольной Collection?
Для произвольной Collection вызов withContiguousStorageIfAvailable не гарантирует, что замыкание будет выполнено: метод может вернуть nil, если непрерывное хранилище недоступно. Если замыкание вызвано, ему временно предоставляется UnsafeBufferPointer на элементы, валидный только во время этого вызова.
Коллекции Swift описывают способ доступа к элементам, но не обязаны хранить их подряд в памяти. Это позволяет реализовывать разные структуры данных, не навязывая каждой из них массивоподобное представление.
Одновременно низкоуровневые алгоритмы иногда выигрывают от работы с непрерывным буфером. withContiguousStorageIfAvailable предоставляет оптимизационный путь без изменения общего контракта Collection и без обязательного копирования элементов.
Код, работающий с произвольной коллекцией, не должен предполагать наличие непрерывной памяти. Если без проверки передать элементы в API, требующий буфер, возможны лишнее копирование, потеря производительности или некорректная работа с небезопасным указателем.
Даже при успешном вызове нельзя сохранять полученный указатель для последующего использования. Его время жизни ограничено выполнением замыкания, а изменения владельца хранилища могут сделать указатель недействительным.
Метод пытается предоставить временное представление элементов как UnsafeBufferPointer. При наличии подходящего хранилища замыкание выполняется, а его результат возвращается как значение, обёрнутое в Optional. При отсутствии такого хранилища замыкание не вызывается, и результатом становится nil.
В примере непрерывный буфер используется как быстрый путь, а обычный обход коллекции — как универсальный запасной вариант. Сам алгоритм сохраняет корректность независимо от конкретного типа коллекции.
Буфер нельзя возвращать из замыкания, сохранять в свойстве или использовать после завершения вызова. Также не следует изменять коллекцию во время работы с предоставленным буфером: это может нарушить эксклюзивный доступ и стабильность адреса элементов.
Главный компромисс — сложность против производительности. Условный быстрый путь полезен для тяжёлых операций, но для простого вычисления дополнительная ветка и использование небезопасного API могут не окупить себя.
Модулю обработки изображений нужно передавать байты в системный алгоритм, принимающий непрерывный буфер. Рассматривались два варианта: всегда создавать Array из входной коллекции или использовать withContiguousStorageIfAvailable с обходом коллекции при неудаче.
Безусловное копирование проще и даёт единый путь выполнения, но увеличивает потребление памяти и задержку для больших данных. Условный буферный путь сложнее, зато для коллекций с подходящим хранилищем не требует промежуточной копии.
Выбран второй вариант: замыкание выполняет работу только в пределах гарантированного времени жизни буфера, а nil приводит к корректному обобщённому обходу. В результате сохраняется поддержка любых коллекций, а для типичных массивов используется более эффективный путь.
Нет. Суффикс IfAvailable является частью контракта: если непрерывное хранилище недоступно, замыкание не запускается, а метод возвращает nil. Поэтому результат нужно обрабатывать как Optional и предусматривать общий алгоритм или другой способ получения данных.
UnsafeBufferPointer для последующего использования?Нет. Указатель гарантированно действителен только внутри замыкания, пока библиотека предоставляет доступ к непрерывному хранилищу. После выхода из замыкания нельзя полагаться ни на адрес памяти, ни на продолжительность жизни самих элементов.
Потому что непрерывность — это физическое свойство конкретного представления, а не необходимая семантика коллекции. Такое требование исключило бы структуры данных с иным хранением и заставило бы их искусственно копировать элементы. Отдельный условный API позволяет получить оптимизацию только там, где она действительно доступна.