1. Understanding Recursive Enumerations in Swift

什么是递归枚举?“间接”关键字做什么?

递归枚举只不过是一种枚举,其case指向枚举本身。

要创建一个,您需要使用indirect关键字。例如:

enum Test {
    indirect case value(Test)
}

你也可以把间接关键字放在枚举声明的前面:

indirect enum Test {
    case value(Test)
}

***indirect***关键字用于告诉Swift以一种允许其增长的方式存储这种特殊类型的枚举。


1.1. Example

让我们创建一个递归枚举示例。在本例中,您将使用形状。

点是最简单的形状。如果你把两个点连起来,你会得到一条同样是形状的直线。 所以两个形状可以用来构造一个新形状。因此,形状需要是递归构造。

有很多方法可以将其转换为代码,但让我们使用递归枚举:

enum Shape {
    case Point(Double, Double)
    indirect case Line(Shape, Shape)
    
    func info() -> String {
        switch self {
        case .Point(let x, let y):
            return "(\(x), \(y))"
        case .Line(let p1, let p2):
            return p1.info() + " <---> " + p2.info()
        }
    }
}

让我们仔细看看代码:

  • 关键是Shape枚举同时表示点和线。 但由于Line也是Shape,它需要递归地引用Shape。这是通过间接案例Line(Shape, Shape)实现的。

  • info()方法只是创建形状的字符串表示。 让我们通过创建三个形状来测试以上内容:两个点和由这些点连接的一条线。

let pStart: Shape = Shape.Point(3.08, 4.23)

let pEnd: Shape = Shape.Point(7.12, 9.32)
let line: Shape = Shape.Line(pStart, pEnd)

print(line.info())

输出是一个从点"(3.0,4.23)"到"(7.12,9.32)"的"行",以字符串形式表示:

(3.0, 4.23) <---> (7.12, 9.32)

1.2. Why the indirect Keyword Is Necessary

如果创建一个没有indirect关键字的递归枚举:

enum Test {
    case value(Test)
}

你会得到一个编译错误:recursive enum 'Test' is not marked 'indirect'

在Swift中,枚举本质上是值类型。这意味着必须在编译期间对它们的内存进行管理。简单地说,必须事先知道枚举所需的确切内存大小。

记住这一点,如果您创建一个递归枚举(即在它内部引用枚举本身),那么您创建的递归是不可能说出枚举需要多少内存的。

这个问题可以通过间接关键字来解决。通过这样做,您告诉Swift更改存储枚举的方式,以允许它的大小增长。


1.3. Conclusion

递归枚举是一种特殊类型的枚举,它可以引用自身。你可以像普通枚举一样创建一个,但是由于Swift的内存管理原因,你需要使用indirect关键字。

For example:

enum Test {
    indirect case value(Test)
}