我很驚訝地發現它可以編譯:
enum Foo {
case one([Foo])
// or
// case one([String: Foo])
}
我本來希望編譯器告訴我添加indirect到列舉案例中,因為Foo包含更多的Foos。Arrays 和Dictionarys 都是值型別,所以這里沒有間接。對我來說,這在結構上類似于:
enum Foo {
case one(Bar<Foo>)
}
// arrays and dictionaries are just a struct with either something or nothing in it
// right?
struct Bar<T> {
let t: T?
}
...這確實需要我添加indirect到列舉中。
然后我嘗試了元組:
enum Foo {
case one((Foo, Foo))
}
這也被認為是遞回列舉。我認為陣列和元組具有相同的記憶體布局,這就是為什么您可以使用withUnsafeBytes然后系結記憶體將陣列轉換為元組...
這遵循什么規則?陣列和字典實際上有什么特別之處嗎?如果是這樣,是否還有其他此類“特殊”型別看起來應該 require indirect,但實際上不需要?
Swift 指南不是很有幫助。它只是說:
遞回列舉是將列舉的另一個實體作為一個或多個列舉情況的關聯值的列舉。您可以通過在列舉 case 之前寫間接來指示它是遞回的,這告訴編譯器插入必要的間接層。
“具有列舉的另一個實體”的含義相當模糊。甚至可以將其解釋case one(Bar<Foo>)為不是遞回的——這種情況下只有 的實體Bar<Foo>,不是Foo。
uj5u.com熱心網友回復:
我認為部分混亂源于這個假設:
我認為陣列和元組具有相同的記憶體布局,這就是為什么你可以使用 withUnsafeBytes 將陣列轉換為元組,然后系結記憶體......
陣列和元組沒有相同的記憶體布局:
Array<T>是一個固定大小struct的指標,指向一個緩沖區,該緩沖區將陣列元素連續*保存在記憶體中- 只有在原生 Swift 陣列的情況下才承諾連續性 [不是從 Objective-C 橋接的]。
NSArray實體不保證它們的底層存盤是連續的,但最終這不會對下面的代碼產生影響。
- 只有在原生 Swift 陣列的情況下才承諾連續性 [不是從 Objective-C 橋接的]。
- 元組是連續保存在記憶體中的元素的固定大小緩沖區
關鍵的是,的大小Array<T>并沒有與保持元件的數目改變(其尺寸僅僅是一個指標,指向緩沖區的大小),而一個元組確實。元組更等同于陣列保存的緩沖區,而不是陣列本身。
Array<T>.withUnsafeBytescall Array<T>.withUnsafeBufferPointer,它回傳指向緩沖區的指標,而不是陣列本身。*(在非連續橋接的情況下NSArray,_ArrayBuffer.withUnsafeBufferPointer必須創建其內容的臨時連續副本,以便向您回傳有效的緩沖區指標。)
在為型別布置記憶體時,編譯器需要知道型別有多大。鑒于上述情況, anArray<Foo>靜態地已知大小是固定的:一個指標的大小(指向記憶體中其他地方的緩沖區)。
給定的
enum Foo {
case one((Foo, Foo))
}
為了布置 的大小Foo,您需要找出其所有案例的最大大小。它只有一個箱子,所以它是那個箱子的大小。
計算的大小one需要計算其關聯值的大小,元素元組的大小是元素本身大小的總和(考慮到填充和對齊,但我們并不真正關心這一點這里)。
因此, 的大小Foo是 的大小one,即(Foo, Foo)在記憶體中布局的大小。那么,尺寸是(Foo, Foo)多少?嗯,它的大小Foo ...的大小,Foo每個是...的大小Foo ...的大小,Foo每個是...的大小Foo Foo...的大小
哪里Array<Foo>有出路(Array<T>無論大小都相同T),我們陷入了沒有基本情況的無限回圈中。
indirect是中斷遞回并為這個無限參考提供基本情況所需的關鍵字。它通過使給定的指標具有固定大小來插入隱式指標,而不管它包含或指向什么。這使得大小固定,這允許有一個固定的大小。caseoneFoo
indirect小于約Foo參照Foo在任何方式,以及如何允許enum 的情況下,以潛在地包含本身間接(因為直接遏制會導致無限回圈)。
順便說一句,這也是為什么 astruct不能包含自身的直接實體的原因:
struct Foo {
let foo: Foo // error: Value type 'Foo' cannot have a stored property that recursively contains it
}
會導致無限遞回,而
struct Foo {
let foo: UnsafePointer<Foo>
}
很好。
struct■不要支持indirect關鍵字(至少在一struct,您對存盤和布局更直接的控制),但也出現了球場上的雨燕論壇提供這方面的支持。
uj5u.com熱心網友回復:
間接列舉情況可用于告訴編譯器將該列舉的關聯值存盤為指標。參考。
當編譯器在不使用指標的情況下無法確定關聯值的記憶體布局時,這是必要的,例如在遞回列舉的情況下。
您需要進行以下情況indirect的原因是因為通過使用通用結構將您的列舉作為列舉本身的情況下的關聯值,您最終會得到一個遞回列舉,因為您可以無限地嵌套Foo和Bar相互嵌套多次。
enum MaybeRecursive {
case array([MaybeRecursive])
case dict([String: MaybeRecursive])
indirect case genericStruct(GenericStruct<MaybeRecursive>)
}
struct GenericStruct<T> {
let t: T?
}
// if you replaced `.array` with `.genericStruct` again, you could keep going forever
let associatedValue = GenericStruct(t: MaybeRecursive.genericStruct(.init(t: .array([]))))
MaybeRecursive.genericStruct(associatedValue)
至于Arrayvs的Tuple區別,Itai Ferber在他們自己的回答中已經熟練地解釋了這一部分。
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/384057.html
