Understanding The ArraySlice in Swift
Today’s article is inspired by a little problem I ran into a few months back while working on a graphing solution for an iOS application. The app displays a ton of data from data sets that tend to be very large. It also needs to support scrolling along the X-axis, meaning that we need to update the graphs accordingly to show what the user is currently interested in seeing.
Depending on how wide of a range the user wants to display, working with arrays can quickly become taxing and waste a lot of computational resources just on copying values. This is where the ArraySlice
comes into play.
What Is An ArraySlice?
ArraySlice
is a generic struct that very much behaves like an array. It supports much of the same functionality that the Array
does, so working with it feels very familiar.
What makes the ArraySlice
special is its capability of free-riding on part of an array’s already allocated memory space. You can visualize it like this:
As you can see, the two arrays each allocates their own space in memory, and keep their own, separate copies of their elements, even though they may be the same (this example disregards any compiler optimizations, like Copy-On-Write, that we may be able to leverage to get two Array
instances to share a memory location).
The ArraySlice
on the other hand, can reference part of another array’s memory and pass it off as its own, effectively eliminating duplication of values. This depends, of course, on the assumption that you are creating your slices by referencing another array. An ArraySlice
is perfectly capable of allocating its own memory space if needed, but then you kind of lose the whole point of using them at all.
Before we dive into some code, let’s take a moment to recognize that an instance of ArraySlice
will actually increase the reference counter for the underlying storage of the array. Because of this, it is never a good idea to store a slice for longer than you absolutely need to, since it will keep the whole memory space from being deallocated if the array is deallocated before our array slice (that means the whole memory space, not just a few elements that the slice may be referencing).
How Do We Use The ArraySlice?
Good question! I mentioned before that ArraySlice
shares a lot of functionality with the Array
struct, so working with it will feel just like home in most aspects.
We start off by creating an ArraySlice
that will reference the 3 middle elements of a 5 element Array
. This can be done in a multitude of ways, but I prefer to do it like this:
var array = [1, 2, 3, 4, 5]
let slice = array[1...3]
print(slice)
// Prints [2, 3, 4]
Now we’ve got an ArraySlice
instance. Remember that it doesn’t create its own copy of the 2, 3, and 4 that it claims to hold, it merely borrows that portion of the array
variable’s memory.
Let’s see if we can use our new slice
variable for something useful, shall we? Let’s start by calculating the sum of all the elements in it:
let sum = slice.reduce(0, +)
print(sum)
// Prints 9
That seems correct. What about the maximum value of the slice?
let max = slice.max()
print(max)
// Prints Optional(4)
That seems to work too! The .max()
method returns an optional value, just in case the array does not contain a maximum value.
How about printing all the number in our slice, one by one?
for index in 0 ..< slice.count {
print(slice[index])
}
// Fatal error: Index out of bounds
Wait, what?
This is actually a pretty interesting detail to know about the ArraySlice
. Since it references another arrays memory space, it also borrows the indices from that array. So the indices of our slice don’t range from 0 through 2, they actually range from 1 through 3!
So, how can we solve this?
We solve it by using a technique that should (in an ideal world) be applied to every instance that can be subscripted. The ArraySlice
class contains two properties named .startIndex
and .endIndex
, which gives us access to the offsets of the start and end of our slice. These will match perfectly with the offsets that the array instance provides for the range of elements we’re referencing. We change our code to look like this…
for index in slice.startIndex ..< slice.endIndex {
print(slice[index])
}
// Prints 2 3 4
… and we’re good to go! One thing to note about the .endIndex
property is that it actually references the first offset after the array ends, which means you will get an Index Out Of Bounds error if you try to access the element in that location. Therefore, use the half-open range operator (..<
) when forming the index range you want to use.
That’s it for this time! Feel free to comment if you have questions, and follow to get notifications about future articles.
To learn more about iOS Development, check out my previous articles:
Follow us on social media platforms:
Facebook: facebook.com/AppCodamobile/
Twitter: twitter.com/AppCodaMobile
Instagram: instagram.com/AppCodadotcom