Сегодняшняя статья основана на небольшой проблеме, с которой я столкнулся несколько месяцев назад, работая над решением для построения графиков для приложения iOS. Приложение отображает массу данных из наборов данных, которые, как правило, очень большие. Он также должен поддерживать прокрутку по оси X, а это означает, что нам необходимо соответствующим образом обновить графики, чтобы показать то, что в данный момент интересно видеть пользователю.
В зависимости от того, насколько широкий диапазон пользователь хочет отобразить, работа с массивами может быстро стать утомительной и тратить много вычислительных ресурсов только на копирование значений. Здесь в игру вступает ArraySlice
.
Что такое ArraySlice?
ArraySlice
- это общая структура, которая очень похожа на массив. Он поддерживает большую часть тех же функций, что и Array
, поэтому работа с ним кажется очень знакомой.
Что делает ArraySlice
особенным, так это его способность бесплатно использовать часть уже выделенного пространства памяти массива. Вы можете представить это так:
Как видите, каждый из двух массивов выделяет свое собственное пространство в памяти и хранит свои собственные отдельные копии своих элементов, даже если они могут быть одинаковыми (в этом примере не учитываются любые оптимизации компилятора, такие как Copy-On-Write, которые возможно, мы сможем использовать два Array
экземпляра для совместного использования места в памяти).
ArraySlice
, с другой стороны, может ссылаться на часть памяти другого массива и выдавать ее за свою, эффективно устраняя дублирование значений. Это, конечно, зависит от предположения, что вы создаете свои срезы, ссылаясь на другой массив. ArraySlice
вполне способен при необходимости выделять собственное пространство памяти, но тогда вы теряете смысл их использования.
Прежде чем мы углубимся в некоторый код, давайте поймем момент, чтобы понять, что экземпляр ArraySlice
фактически увеличивает счетчик ссылок для базового хранилища массива. Из-за этого никогда не рекомендуется хранить срез дольше, чем вам абсолютно необходимо, поскольку это будет препятствовать освобождению всего пространства памяти, если массив будет освобожден до нашего среза массива (это означает, что все пространство памяти, а не просто несколько элементов, на которые может ссылаться срез).
Как мы используем ArraySlice?
Хороший вопрос! Я упоминал ранее, что ArraySlice
имеет много общего со структурой Array
, поэтому работа с ней будет во многих аспектах чувствовать себя как дома.
Мы начинаем с создания ArraySlice
, который будет ссылаться на 3 средних элемента из 5 элементов Array
. Это можно сделать множеством способов, но я предпочитаю делать это так:
var array = [1, 2, 3, 4, 5] let slice = array[1...3] print(slice) // Prints [2, 3, 4]
Теперь у нас есть ArraySlice
экземпляр. Помните, что он не создает свою собственную копию 2, 3 и 4, которые, по его утверждению, хранятся, он просто заимствует эту часть памяти array
переменной.
Посмотрим, сможем ли мы использовать нашу новую переменную slice
для чего-нибудь полезного? Начнем с вычисления суммы всех элементов в нем:
let sum = slice.reduce(0, +) print(sum) // Prints 9
Это кажется правильным. А как насчет максимального значения среза?
let max = slice.max() print(max) // Prints Optional(4)
Кажется, это тоже работает! Метод .max()
возвращает необязательное значение на тот случай, если массив не содержит максимального значения.
Как насчет того, чтобы напечатать все числа в нашем срезе один за другим?
for index in 0 ..< slice.count { print(slice[index]) } // Fatal error: Index out of bounds
Чего ждать?
На самом деле это довольно интересная деталь о ArraySlice
. Поскольку он ссылается на пространство памяти другого массива, он также заимствует индексы из этого массива. Таким образом, индексы нашего среза меняются не от 0 до 2, а от 1 до 3!
Итак, как мы можем это решить?
Мы решаем эту проблему, используя технику, которая (в идеальном мире) должна применяться к каждому экземпляру, который может быть подписан. Класс ArraySlice
содержит два свойства с именами .startIndex
и .endIndex
, которые дают нам доступ к смещениям начала и конца нашего фрагмента. Они будут идеально соответствовать смещениям, которые экземпляр массива предоставляет для диапазона элементов, на которые мы ссылаемся. Мы меняем наш код, чтобы он выглядел так…
for index in slice.startIndex ..< slice.endIndex { print(slice[index]) } // Prints 2 3 4
… И мы в порядке! Следует отметить, что свойство .endIndex
фактически ссылается на первое смещение после окончания массива, что означает, что вы получите ошибку индекса за пределами границ, если попытаетесь получить доступ к элементу в этом месте. Поэтому используйте оператор полуоткрытого диапазона (..<
) при формировании диапазона индекса, который вы хотите использовать.
На этот раз все! Не стесняйтесь комментировать, если у вас есть вопросы, и подписывайтесь, чтобы получать уведомления о будущих статьях.
Чтобы узнать больше о разработке для iOS, ознакомьтесь с моими предыдущими статьями:
Подпишитесь на нас в социальных сетях:
Facebook: facebook.com/AppCodamobile/
Twitter: twitter.com/AppCodaMobile
Instagram: instagram. ru / AppCodadotcom