Swift @functionBuilder
SwiftUI 允许我们写出这样的代码:
VStack {
Text("Line 1")
Spacer().frame(height: 16)
Text("Line 2")
}
首先 VStack { /* ... */ } 实际是初始化了一个 VStack。这个初始化函数的最后一个参数是一个闭包,这个闭包需要返回 View 类型的对象。
这是它的定义:
/// A view that arranges its children in a vertical line.
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
@frozen public struct VStack<Content> : View where Content : View {
@inlinable public init(alignment: HorizontalAlignment = .center, spacing: CGFloat? = nil, @ViewBuilder content: () -> Content)
public typealias Body = Never
}
这里有两个疑点:
- 闭包的类型是
() -> Content,而Content被where从句限制为服从View协议的类型,因此可以认为闭包content返回的应是some View,而我们却返回了三个View对象。 - 而且就算要返回多个
View,按照经验,我们可以选择元组或其他一些容器对象,而此处我们却直接写在了{ /* 这里面 */ },甚至中间连个逗号都没有
看来看去也就这个 @ViewBuilder 最为可疑了,查看它的定义:
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
@_functionBuilder public struct ViewBuilder {
/// Builds an empty view from an block containing no statements, `{ }`.
public static func buildBlock() -> EmptyView
/// Passes a single view written as a child view (e..g, `{ Text("Hello") }`) through
/// unmodified.
public static func buildBlock<Content>(_ content: Content) -> Content where Content : View
}
它是一个 @_functionBuilder 标注的 struct,仿照它的格式,我尝试写了一个 TensorBuilder:
@_functionBuilder struct TensorBuilder {
static func buildBlock<T>(_ tensors: T...) -> [T] {
tensors
}
}
然后将它加到 Tensor 的初始化函数中去(Tensor 只不过是 Array 的 alias 而已):
typealias Tensor = Array
extension Tensor {
init(@TensorBuilder _ builder: () -> Self) {
self = builder()
}
}
TensorBuilder 的功能就是把输入的元素“串”起来,比如将三个标量变成一个向量:
let vector = Tensor {
0
1
2
}
// [0, 1, 2]
将两个向量组成矩阵:
let matrix = Tensor {
vector
[3,4,5]
}
// [[0, 1, 2], [3, 4, 5]]
将两个矩阵组成张量:
let tensor = Tensor {
matrix
Tensor {
[6,7,8]
[9,10,11]
}
}
// [[[0, 1, 2], [3, 4, 5]], [[6, 7, 8], [9, 10, 11]]]
后面还能组成阶数更高的张量。
这个功能算是处于半正式公开的状态,能用,但是前面还有个 _。
我的这个例子比较没意思(也没啥用),这里有很多别人写的 @_functionBuilder:
awesome-function-builders
比如 HTML-DSL:
html(lang: "en-US") {
body(customData: ["hello":"world"]) {
article(classes: "readme", "modern") {
h1 {
"Hello World"
}
}
}
}
比如 NSAttributedStringBuilder:
NSAttributedString {
AText("Hello world")
.font(.systemFont(ofSize: 24))
.foregroundColor(.red)
LineBreak()
AText("with Swift")
.font(.systemFont(ofSize: 20))
.foregroundColor(.orange)
}