File: Arrays.md

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (246 lines) | stat: -rw-r--r-- 9,940 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
---
author: "Dave Abrahams"
date: "2014-04-10"
---

# The Swift Array Design

## Goals

1.  Performance equivalent to C arrays for subscript get/set of
    non-class element types is the most important performance goal.
2.  It should be possible to receive an `NSArray` from Cocoa, represent
    it as an `Array<AnyObject>`, and pass it right back to Cocoa as an
    `NSArray` in O(1) and with no memory allocations.
3.  Arrays should be usable as stacks, so we want amortized O(1) append
    and O(1) popBack. Together with goal #1, this implies a
    `std::vector`-like layout, with a reserved tail memory capacity that
    can exceed the number of actual stored elements.

To achieve goals 1 and 2 together, we use static knowledge of the
element type: when it is statically known that the element type is not a
class, code and checks accounting for the possibility of wrapping an
`NSArray` are eliminated. An `Array` of Swift value types always uses
the most efficient possible representation, identical to that of
`ContiguousArray`.

## Components

Swift provides three generic array types, all of which have amortized
O(1) growth. In this document, statements about **ArrayType** apply to
all three of the components.

-   `ContiguousArray<Element>` is the fastest and simplest of the
    three\--use this when you need \"C array\" performance. The elements
    of a `ContiguousArray` are always stored contiguously in memory.

    ![image](ContiguousArray.png)

-   `Array<Element>` is like `ContiguousArray<Element>`, but optimized
    for efficient conversions from Cocoa and back\--when `Element` can
    be a class type, `Array<Element>` can be backed by the (potentially
    non-contiguous) storage of an arbitrary `NSArray` rather than by a
    Swift `ContiguousArray`. `Array<Element>` also supports up- and
    downcasts between arrays of related class types. When `Element` is
    known to be a non-class type, the performance of `Array<Element>` is
    identical to that of `ContiguousArray<Element>`.

    ![image](ArrayImplementation.png)

-   `ArraySlice<Element>` is a subrange of some `Array<Element>` or
    `ContiguousArray<Element>`; it\'s the result of using slice
    notation, e.g. `a[7...21]` on any Swift array `a`. A slice always
    has contiguous storage and \"C array\" performance. Slicing an
    *ArrayType* is O(1) unless the source is an `Array<Element>` backed
    by an `NSArray` that doesn\'t supply contiguous storage.

    `ArraySlice` is recommended for transient computations but not for
    long-term storage. Since it references a sub-range of some shared
    backing buffer, a `ArraySlice` may artificially prolong the lifetime
    of elements outside the `ArraySlice` itself.

    ![image](Slice.png)

## Mutation Semantics

The *ArrayType*s have full value semantics via copy-on-write (COW):

```swift
var a = [1, 2, 3]
let b = a
a[1] = 42
print(b[1]) // prints "2"
```

## Bridging Rules and Terminology for all Types

-   Every class type or `@objc` existential (such as `AnyObject`) is
    **bridged** to Objective-C and **bridged back** to Swift via the
    identity transformation, i.e. it is **bridged verbatim**.

-   A type `T` that is not [bridged verbatim](#bridging-rules-and-terminology-for-all-types) 
    can conform to `BridgedToObjectiveC`, which specifies its conversions to
    and from Objective-C:

    ```swift
    protocol _BridgedToObjectiveC {
        typealias _ObjectiveCType: AnyObject
        func _bridgeToObjectiveC() -> _ObjectiveCType
        class func _forceBridgeFromObjectiveC(_: _ObjectiveCType) -> Self
    }
    ```

    ### Note

    > Classes and `@objc` existentials shall not conform to
    `_BridgedToObjectiveC`, a restriction that\'s not currently
    enforceable at compile-time.

-   Some generic types (`Array<T>` in particular) bridge to
    Objective-C only if their element types bridge. These types conform
    to `_ConditionallyBridgedToObjectiveC`:

    ```swift
    protocol _ConditionallyBridgedToObjectiveC : _BridgedToObjectiveC {
        class func _isBridgedToObjectiveC() -> Bool
        class func _conditionallyBridgeFromObjectiveC(_: _ObjectiveCType) -> Self?
    }
    ```

    Bridging from, or *bridging back* to, a type `T` conforming to
    `_ConditionallyBridgedToObjectiveC` when
    `T._isBridgedToObjectiveC()` is `false` is a user programming error
    that may be diagnosed at runtime.
    `_conditionallyBridgeFromObjectiveC` can be used to attempt to
    bridge back, and return `nil` if the entire object cannot be
    bridged.

    ### Implementation Note

    There are various ways to move this detection to compile-time

    -   For a type `T` that is not [bridged verbatim](#bridging-rules-and-terminology-for-all-types),

        -   if `T` conforms to `BridgedToObjectiveC` and either

            -   `T` does not conform to `_ConditionallyBridgedToObjectiveC`
            -   or, `T._isBridgedToObjectiveC()`

            then a value `x` of type `T` is **bridged** as
            `T._ObjectiveCType` via `x._bridgeToObjectiveC()`, and an object
            `y` of `T._ObjectiveCType` is **bridged back** to `T` via
            `T._forceBridgeFromObjectiveC(y)`

        -   Otherwise, `T` **does not bridge** to Objective-C

## `Array` Type Conversions

From here on, this document deals only with `Array` itself, and not
`Slice` or `ContiguousArray`, which support a subset of `Array`\'s
conversions. Future revisions will add descriptions of `Slice` and
`ContiguousArray` conversions.

### Kinds of Conversions

In these definitions, `Base` is `AnyObject` or a trivial subtype
thereof, `Derived` is a trivial subtype of `Base`, and `X` conforms to
`_BridgedToObjectiveC`:

-   **Trivial bridging** implicitly converts `[Base]` to `NSArray` in
    O(1). This is simply a matter of returning the Array\'s internal
    buffer, which is-a `NSArray`.

-   **Trivial bridging back** implicitly converts `NSArray` to
    `[AnyObject]` in O(1) plus the cost of calling `copy()` on the
    `NSArray`.[^1]

-   **Implicit conversions** between `Array` types

    -   **Implicit upcasting** implicitly converts `[Derived]` to
        `[Base]` in O(1).
    -   **Implicit bridging** implicitly converts `[X]` to
        `[X._ObjectiveCType]` in O(N).

    ### Note

    > Either type of implicit conversion may be combined with [trivial
    bridging](#trivial bridging) in an implicit conversion to `NSArray`.

-   **Checked conversions** convert `[T]` to `[U]?` in O(N) via
    `a as [U]`.

    -   **Checked downcasting** converts `[Base]` to `[Derived]?`.
    -   **Checked bridging back** converts `[T]` to `[X]?` where
        `X._ObjectiveCType` is `T` or a trivial subtype thereof.

-   **Forced conversions** convert `[AnyObject]` or `NSArray` to `[T]`
    implicitly, in bridging thunks between Swift and Objective-C.

    For example, when a user writes a Swift method taking `[NSView]`, it
    is exposed to Objective-C as a method taking `NSArray`, which is
    force-converted to `[NSView]` when called from Objective-C.

    -   **Forced downcasting** converts `[AnyObject]` to `[Derived]` in
        O(1)
    -   **Forced bridging back** converts `[AnyObject]` to `[X]` in
        O(N).

    A forced conversion where any element fails to convert is considered
    a user programming error that may trap. In the case of forced
    downcasts, the trap may be [deferred](#deferred-checking-for-forced-downcasts)
    to the point where an offending element is accessed.

### Note

Both checked and forced downcasts may be combined with [trivial bridging
back](#trivial bridging back) in conversions from `NSArray`.

### Maintaining Type-Safety

Both upcasts and forced downcasts raise type-safety issues.

#### Upcasts

TODO: this section is outdated.

When up-casting an `[Derived]` to `[Base]`, a buffer of `Derived` object
can simply be `unsafeBitCast`\'ed to a buffer of elements of type
`Base`\--as long as the resulting buffer is never mutated. For example,
we cannot allow a `Base` element to be inserted in the buffer, because
the buffer\'s destructor will destroy the elements with the (incorrect)
static presumption that they have `Derived` type.

Furthermore, we can\'t (logically) copy the buffer just prior to
mutation, since the `[Base]` may be copied prior to mutation, and our
shared subscript assignment semantics imply that all copies must observe
its subscript assignments.

Therefore, converting `[T]` to `[U]` is akin to resizing: the new
`Array` becomes logically independent. To avoid an immediate O(N)
conversion cost, and preserve shared subscript assignment semantics, we
use a layer of indirection in the data structure. Further, when `T` is a
subclass of `U`, the intermediate object is marked to prevent in-place
mutation of the buffer; it will be copied upon its first mutation:

![image](ArrayCast.png)

#### Deferred Checking for Forced Downcasts

In forced downcasts, if any element fails to have dynamic type
`Derived`, it is considered a programming error that may cause a trap.
Sometimes we can do this check in O(1) because the source holds a known
buffer type. Rather than incur O(N) checking for the other cases, the
new intermediate object is marked for deferred checking, and all element
accesses through that object are dynamically typechecked, with a trap
upon failure (except in `-Ounchecked` builds).

When the resulting array is later up-cast (other than to a type that can
be validated in O(1) by checking the type of the underlying buffer), the
result is also marked for deferred checking.

------------------------------------------------------------------------

[^1]: This `copy()` may amount to a retain if the `NSArray` is already
    known to be immutable. We could eventually optimize out the copy if
    we can detect that the `NSArray` is uniquely referenced. Our current
    unique-reference detection applies only to Swift objects, though.