-
Notifications
You must be signed in to change notification settings - Fork 7
Arrays
Arrays in micro Swift are different from "regular" Swift, for memory and performance reasons.
They are more like C arrays, defined in a fixed size once, then released manually when no longer required.
You can define an array using one of the following approaches...
- with an array literal
- variadic arguments to a function
- "dynamically", by calling Array.init(repeating)
Any of these will create a fixed sized buffer on the heap, based on what the array is "made of" and how many slots it contains, for example:
struct MyStruct {
var v1...
var v2...
...etc...
static var templateValue: MyStruct { ... }
}
var myArray = [MyStruct](repeating: templateValue, count: 99)
...will create an array that consumes 99 * MemoryLayout<MyStruct>.size
bytes on the heap.
==> you are responsible for releasing this memory!!! <==
ARC and automated memory management do not exist on this platform. In order to create a temporary array then return the memory, you must call release
on the instance when you are done with it.
Note: in most cases, it's economical and appropriate to have the array be a global level object that exists for the lifetime of the program. Try not to think of always discarding arrays and recreating them later as inexpensive operations. Memory and CPU are scarce resources on these platforms and to maintain hard real time performance, we cannot allow unbounded or unpredictable runtime execution paths.
You may be used to powerful constructs like let array1 = [1, "smith", UIColor.red, 3.4411]
. These are not supported in micro swift. The reason is this would be initialised as an array of [Any]
and that is not supported. It will compile at first but give a slew of runtime errors. Any
is an example of an "existential container". A variable typed to a protocol can also be one of these. They do not have a fixed layout in memory like a normal static struct where the compiler knows the memory layout in advance. Instead, they have a variable layout covering something like 16 bytes, that sometimes contains a data instance and sometimes a pointer to a side allocated heap buffer with the data instance. All methods are not statically linked but use a "witness table", which is much like a vtable. None of this is supported in the runtime.
To avoid this, make sure you only use one genuine, fully reified type for array literals. If in doubt, clearly specify the type such as let array2: [Float] = [1, 3.452234, 99.7]
. If you pass any type that is not compatible then you'll get a clear compiler error and can restructure your code. One approach to "get round" this limitation in some cases is to use enums with differing associated types.
Arrays can be used as you would normally in swift. Index into them, iterate over them. They also have bounds checking, like swift but unlike regular swift, micro swift tries to fail gracefully with bounds checking clamping the indexes. So if an array has 10 elements and you index myArray[11]
then micro swift will just return the final element of the array (myArray[9]
). This can have unintended consequences so be careful, but is probably more useful than the microcontroller hanging.
You can update values inside an array following the normal rules. If you have 10 values in an array and you set myArray[2] = 778
then the value at position 2 will be overwritten. If you try to write outside bounds then as above, your write will be clamped to the first or last elements of the array.
You cannot append to an array, concatenate arrays, etc. This is because the swift version of dynamic arrays will allocate memory from the heap when it sees fit, not under your control, and that is not acceptable or safe on a microcontroller.
You are probably used to thinking of arrays in swift as being semantically a value type. This is not the case with micro swift. All variables copied from one another point to the same underlying storage so are effectively reference types. The COW mechanism you may expect on swift is not present in micro swift.
If you write...
let array1 = [1,5,9]
var array2 = array1
array2[1] = 886
Then both array2
AND array1
will end up with the values [1,886,9] in them. This will be one of the biggest surprises if you're used to swift programming with arrays!
(Again, it is for memory, performance and predictability reasons... a COW write could suddenly call into array duplication with an unexpectedly large delay, just in the middle of a timing critical loop.)