我认为部分混乱源于这个假设:
我认为数组和元组具有相同的内存布局,这就是为什么您可以使用 withUnsafeBytes 将数组转换为元组,然后绑定内存...
数组和元组don't具有相同的内存布局:
-
Array<T>
is a fixed-size struct
with a pointer to a buffer which holds the array elements contiguously* in memory
- 仅在本机 Swift 数组[未从 Objective-C 桥接]的情况下才保证连续性。
NSArray
实例不保证其底层存储是连续的,但最终这不会对下面的代码产生影响。
- 元组是内存中连续保存的元素的固定大小缓冲区
关键是一个的大小Array<T>
does not随着所持有元素的数量而变化(它的大小只是指向缓冲区的指针的大小),而元组does。元组更相当于buffer数组保存,而不是数组本身。
Array<T>.withUnsafeBytes calls Array<T>.withUnsafeBufferPointer,它返回指向缓冲区的指针,而不是数组本身。 *(在非连续桥接的情况下NSArray
, _ArrayBuffer.withUnsafeBufferPointer必须创建其内容的临时连续副本为了向您返回有效的缓冲区指针。)
当为类型分配内存时,编译器需要知道类型有多大。鉴于上述情况,Array<Foo>
静态地知道其大小是固定的:一个指针的大小(指向内存中其他位置的缓冲区)。
Given
enum Foo {
case one((Foo, Foo))
}
为了布置尺寸Foo
,您需要计算出其所有案例的最大尺寸。它只有一个案例,因此它的大小就是该案例的大小。
计算出尺寸one
需要计算出其关联值的大小,而元素元组的大小是元素本身大小的总和(考虑到填充和对齐,但我们在这里并不真正关心这一点)。
因此,大小为Foo
的大小是one
,其大小为(Foo, Foo)
布置在记忆里。那么,尺寸是多少(Foo, Foo)
?嗯,它的大小是Foo
+ 的大小Foo
...每个的大小Foo
+ 的大小Foo
...每个的大小Foo
+ 的大小Foo
...
Where Array<Foo>
有出路(Array<T>
大小相同,无论T
),我们陷入了没有基本情况的无限循环。
indirect
是打破递归并为这个无限引用提供基本情况所需的关键字。它插入一个隐式的pointer通过给定case
the fixed指针的大小,无论它包含什么或指向什么。这使得大小为one
固定,这允许Foo
具有固定的大小。
indirect
较少关于Foo
指的是Foo
in any方式,以及更多关于允许enum
案例可能包含itself间接地(因为direct遏制会导致无限循环)。
顺便说一句,这也是为什么struct
不能包含其自身的直接实例:
struct Foo {
let foo: Foo // error: Value type 'Foo' cannot have a stored property that recursively contains it
}
会导致无限递归,而
struct Foo {
let foo: UnsafePointer<Foo>
}
is fine.
struct
不支持indirect
关键字(至少在struct
,您可以更直接地控制存储和布局),但已经有pitches在 Swift 论坛上添加对此的支持。