Сегодняшняя статья основана на небольшой проблеме, с которой я столкнулся несколько месяцев назад, работая над решением для построения графиков для приложения 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

Если вам понравилась эта статья, нажмите кнопку 👏 и поделитесь ею, чтобы помочь другим найти ее! Не стесняйтесь, чтобы оставить комментарий ниже.