{"data":{"post":{"title":"SwiftUI 探秘 - SwiftUI 的编程语言本质","subtitle":"","isPublished":true,"createdTime":"2022-03-06T00:00:00.000Z","lastModifiedTime":null,"license":null,"tags":["SwiftUI 探秘","SwiftUI","Swift"],"category":"编程","file":{"childMdx":{"excerpt":"前言 苹果在 WWDC 2019 向开发者介绍了 SwiftUI。多数人也许会将 SwiftUI 看成又一个如  Flutter  或者  React.js  又或者  Vue.js…","code":{"body":"function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\n\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n\nconst layoutProps = {};\nreturn class MDXContent extends React.Component {\n  constructor(props) {\n    super(props);\n    this.layout = null;\n  }\n\n  render() {\n    const _this$props = this.props,\n          {\n      components\n    } = _this$props,\n          props = _objectWithoutProperties(_this$props, [\"components\"]);\n\n    return React.createElement(MDXTag, {\n      name: \"wrapper\",\n      components: components\n    }, React.createElement(MDXTag, {\n      name: \"h2\",\n      components: components\n    }, `前言`), React.createElement(MDXTag, {\n      name: \"p\",\n      components: components\n    }, `苹果在 WWDC 2019 向开发者介绍了 SwiftUI。多数人也许会将 SwiftUI 看成又一个如 `, React.createElement(MDXTag, {\n      name: \"em\",\n      components: components,\n      parentName: \"p\"\n    }, `Flutter`), ` 或者 `, React.createElement(MDXTag, {\n      name: \"em\",\n      components: components,\n      parentName: \"p\"\n    }, `React.js`), ` 又或者 `, React.createElement(MDXTag, {\n      name: \"em\",\n      components: components,\n      parentName: \"p\"\n    }, `Vue.js`), ` 这样踩在声明式、无状态 UI 编程潮流浪尖的 UI 框架。虽然 SwiftUI 与上述框架有着非常多的共同点，但是 SwiftUI 从设计到实现上都与上述框架有着本质的不同。`), React.createElement(MDXTag, {\n      name: \"p\",\n      components: components\n    }, `实际上，相较于是一个编程框架，SwiftUI 更加像是一种编程语言。不相信？让我来看看一个用「原生」SwiftUI 代码编写的斐波那契数列计算程序。`), React.createElement(MDXTag, {\n      name: \"pre\",\n      components: components\n    }, React.createElement(MDXTag, {\n      name: \"code\",\n      components: components,\n      parentName: \"pre\",\n      props: {\n        \"className\": \"language-swift\"\n      }\n    }, `import SwiftUI\n\nstruct ContentView: View {\n\n    var body: some View {\n        Fibonacci(ordinal: 10)\n    }\n\n}\n\nstruct Fibonacci: View {\n\n    var ordinal: Int\n\n    private var value: Value\n\n    init(ordinal: Int) {\n        self.init(ordinal: ordinal, value: .zero)\n    }\n\n    private init(ordinal: Int, value: Value) {\n        self.ordinal = ordinal\n        self.value = value\n    }\n\n    var body: some View {\n        if ordinal == 0 {\n            Text(\"\\\\(value.description)\")\n        } else {\n            Fibonacci(ordinal: ordinal - 1, value: value.next())\n        }\n    }\n\n    private enum Value: CustomStringConvertible {\n\n        case zero\n\n        case one\n\n        case more(last: Int, current: Int)\n\n        func next() -> Value {\n            switch self {\n            case .zero: return .one\n            case .one:  return .more(last: 0, current: 1)\n            case .more(let last, let current):\n                return .more(last: current, current: last + current)\n            }\n        }\n\n        var description: String {\n            switch self {\n            case .zero:                         return \"0\"\n            case .one:                          return \"1\"\n            case .more(let last, let current):  return \"\\\\(last + current)\"\n            }\n        }\n\n    }\n\n}\n`)), React.createElement(MDXTag, {\n      name: \"p\",\n      components: components\n    }, `然后通过添加以下两行代码，我们可以在 Swift Playground 预览上面的代码。`), React.createElement(MDXTag, {\n      name: \"pre\",\n      components: components\n    }, React.createElement(MDXTag, {\n      name: \"code\",\n      components: components,\n      parentName: \"pre\",\n      props: {\n        \"className\": \"language-swift\"\n      }\n    }, `import PlaygroundSupport\n\nPlaygroundPage.current.setLiveView(ContentView())\n`)), React.createElement(MDXTag, {\n      name: \"p\",\n      components: components\n    }, React.createElement(MDXTag, {\n      name: \"figure\",\n      components: components,\n      parentName: \"p\"\n    }, `\n    `, React.createElement(MDXTag, {\n      name: \"a\",\n      components: components,\n      parentName: \"figure\",\n      props: {\n        \"href\": \"/static/c41ed74d5550f47ced8c9b7efbde17e8/94001/fibonacci-number-on-swift-playground.webp\"\n      }\n    }, React.createElement(MDXTag, {\n      name: \"picture\",\n      components: components,\n      parentName: \"a\"\n    }, `\n  `, React.createElement(MDXTag, {\n      name: \"source\",\n      components: components,\n      parentName: \"picture\",\n      props: {\n        \"src\": \"/static/c41ed74d5550f47ced8c9b7efbde17e8/4ae7e/fibonacci-number-on-swift-playground.jpeg\",\n        \"srcSet\": [\"/static/c41ed74d5550f47ced8c9b7efbde17e8/aeae1/fibonacci-number-on-swift-playground.jpeg 178w\", \"/static/c41ed74d5550f47ced8c9b7efbde17e8/3465b/fibonacci-number-on-swift-playground.jpeg 356w\", \"/static/c41ed74d5550f47ced8c9b7efbde17e8/4ae7e/fibonacci-number-on-swift-playground.jpeg 712w\", \"/static/c41ed74d5550f47ced8c9b7efbde17e8/01d22/fibonacci-number-on-swift-playground.jpeg 1068w\", \"/static/c41ed74d5550f47ced8c9b7efbde17e8/accbd/fibonacci-number-on-swift-playground.jpeg 1424w\", \"/static/c41ed74d5550f47ced8c9b7efbde17e8/c44b2/fibonacci-number-on-swift-playground.jpeg 2136w\", \"/static/c41ed74d5550f47ced8c9b7efbde17e8/ac69a/fibonacci-number-on-swift-playground.jpeg 2732w\"],\n        \"sizes\": \"(max-width: 712px) 100vw, 712px\"\n      }\n    }), React.createElement(MDXTag, {\n      name: \"source\",\n      components: components,\n      parentName: \"picture\",\n      props: {\n        \"src\": \"/static/c41ed74d5550f47ced8c9b7efbde17e8/943f6/fibonacci-number-on-swift-playground.webp\",\n        \"srcSet\": [\"/static/c41ed74d5550f47ced8c9b7efbde17e8/b1d2a/fibonacci-number-on-swift-playground.webp 178w\", \"/static/c41ed74d5550f47ced8c9b7efbde17e8/c91cd/fibonacci-number-on-swift-playground.webp 356w\", \"/static/c41ed74d5550f47ced8c9b7efbde17e8/943f6/fibonacci-number-on-swift-playground.webp 712w\", \"/static/c41ed74d5550f47ced8c9b7efbde17e8/8a6dd/fibonacci-number-on-swift-playground.webp 1068w\", \"/static/c41ed74d5550f47ced8c9b7efbde17e8/fee22/fibonacci-number-on-swift-playground.webp 1424w\", \"/static/c41ed74d5550f47ced8c9b7efbde17e8/fbb66/fibonacci-number-on-swift-playground.webp 2136w\", \"/static/c41ed74d5550f47ced8c9b7efbde17e8/94001/fibonacci-number-on-swift-playground.webp 2732w\"],\n        \"sizes\": \"(max-width: 712px) 100vw, 712px\"\n      }\n    }), `\n  `, React.createElement(MDXTag, {\n      name: \"img\",\n      components: components,\n      parentName: \"picture\",\n      props: {\n        \"src\": \"/static/c41ed74d5550f47ced8c9b7efbde17e8/94001/fibonacci-number-on-swift-playground.webp\",\n        \"alt\": \"在 SwiftUI Playground 中计算斐波那契数列\",\n        \"title\": \"在 SwiftUI Playground 中计算斐波那契数列\",\n        \"width\": 712,\n        \"height\": 534,\n        \"loading\": \"lazy\"\n      }\n    }))), `\n    `, React.createElement(MDXTag, {\n      name: \"figcaption\",\n      components: components,\n      parentName: \"figure\"\n    }, `\n        `, React.createElement(MDXTag, {\n      name: \"span\",\n      components: components,\n      parentName: \"figcaption\"\n    }, `\n            在 SwiftUI Playground 中计算斐波那契数列\n        `), `\n    `))), React.createElement(MDXTag, {\n      name: \"p\",\n      components: components\n    }, `如你所见，我们使用「原生」 SwiftUI 代码计算出了斐波那契数列。但是可以使用来自 SwiftUI 的东西写出一个程序并不意味着 SwiftUI 是一门编程语言。只有当 SwiftUI 为这个程序提供了真正的「语言结构」时，我们才能说 SwiftUI 是一门编程语言。`), React.createElement(MDXTag, {\n      name: \"h2\",\n      components: components\n    }, `View 协议中未被解释的部分`), React.createElement(MDXTag, {\n      name: \"p\",\n      components: components\n    }, `要理解 SwiftUI 的语言结构，我们必须首先理解 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `View`), ` 协议中没有被解释过的部分。`), React.createElement(MDXTag, {\n      name: \"p\",\n      components: components\n    }, `基本上，`, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `View`), ` 协议会如下所示：`), React.createElement(MDXTag, {\n      name: \"pre\",\n      components: components\n    }, React.createElement(MDXTag, {\n      name: \"code\",\n      components: components,\n      parentName: \"pre\",\n      props: {\n        \"className\": \"language-swift\"\n      }\n    }, `public protocol View {\n\n    associatedtype Body: View\n\n    @ViewBuilder\n    var body: Body { get }\n\n}\n`)), React.createElement(MDXTag, {\n      name: \"p\",\n      components: components\n    }, `我们可以看到，一旦一个类型遵从了 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `View`), ` 协议，它的实例将拥有一个 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `body`), ` 属性。同时这个 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `body`), ` 属性也将返回一个遵从 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `View`), ` 协议的实例。这将导致一个有趣的现象：如果我们获得了一个 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `View`), ` 实例的 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `body`), `，那么我们将又能获得这个 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `View`), ` 实例 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `body`), ` 的 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `body`), `。因为 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `View`), ` 实例 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `body`), ` 的 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `body`), ` 也是一个 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `View`), ` 实例，所以我们可以继续获得它的 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `body`), `。理论上，我们可以无限重复这个模式。`), React.createElement(MDXTag, {\n      name: \"p\",\n      components: components\n    }, React.createElement(MDXTag, {\n      name: \"figure\",\n      components: components,\n      parentName: \"p\"\n    }, `\n    `, React.createElement(MDXTag, {\n      name: \"a\",\n      components: components,\n      parentName: \"figure\",\n      props: {\n        \"href\": \"/static/ed9ef8756a06e8f4ff1002d36f5dae1f/07830/execution-pattern.png\"\n      }\n    }, React.createElement(MDXTag, {\n      name: \"img\",\n      components: components,\n      parentName: \"a\",\n      props: {\n        \"title\": \"SwiftUI 的执行模式\",\n        \"alt\": \"SwiftUI 的执行模式\",\n        \"src\": \"/static/ed9ef8756a06e8f4ff1002d36f5dae1f/07830/execution-pattern.png\",\n        \"srcSet\": [\"/static/ed9ef8756a06e8f4ff1002d36f5dae1f/07830/execution-pattern.png 1x\", \"/static/2872dc5f74db1723e7c06b333cd5d327/0ef38/execution-pattern%402x.png 2x\", \"/static/4989517322c2b7747b7a0398b7655dc5/97658/execution-pattern%403x.png 3x\"],\n        \"loading\": \"lazy\"\n      }\n    })), `\n    `, React.createElement(MDXTag, {\n      name: \"figcaption\",\n      components: components,\n      parentName: \"figure\"\n    }, `\n        `, React.createElement(MDXTag, {\n      name: \"span\",\n      components: components,\n      parentName: \"figcaption\"\n    }, `\n            SwiftUI 的执行模式\n        `), `\n    `))), React.createElement(MDXTag, {\n      name: \"p\",\n      components: components\n    }, `是的。你也许已经意识到了 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `View`), ` 协议的设计是递归的。实际上，这个递归的设计提供了 SwiftUI 的基本语言结构。而这个基本语言结构引入了一种可以递归的执行模式。SwiftUI 的内部机制可以持连不断地续获得 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `View`), ` 实例的 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `body`), ` 直到递归结束。`), React.createElement(MDXTag, {\n      name: \"p\",\n      components: components\n    }, `但是什么时候递归结束呢？`), React.createElement(MDXTag, {\n      name: \"p\",\n      components: components\n    }, `SwiftUI 扩展了标准库中的 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `Never`), ` 类型以遵从 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `View`), ` 协议。在这个扩展中，`, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `Never`), ` 的 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `body`), ` 同时会返回 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `Never`), `。所以我们可以知道这就是递归的终点。`), React.createElement(MDXTag, {\n      name: \"pre\",\n      components: components\n    }, React.createElement(MDXTag, {\n      name: \"code\",\n      components: components,\n      parentName: \"pre\",\n      props: {\n        \"className\": \"language-swift\"\n      }\n    }, `extension Never: View { }\n\nextension Never {\n\n    public typealias Body = Never\n\n    public var body: Never { get }\n\n}\n`)), React.createElement(MDXTag, {\n      name: \"p\",\n      components: components\n    }, `在 SwiftUI 的内部实现中，一旦一个 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `View`), ` 实例的 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `body`), ` 是 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `Never`), ` 类型的，SwiftUI 将停止获取这个 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `View`), ` 实例的 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `body`), `，转而切换到这个视图的`, React.createElement(MDXTag, {\n      name: \"strong\",\n      components: components,\n      parentName: \"p\"\n    }, `内建`), `逻辑。如果这个视图的`, React.createElement(MDXTag, {\n      name: \"strong\",\n      components: components,\n      parentName: \"p\"\n    }, `内建`), `逻辑不会将执行转交给其他 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `View`), ` 实例，那么递归将终止。`), React.createElement(MDXTag, {\n      name: \"p\",\n      components: components\n    }, `SwiftUI 带来了很多 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `body`), ` 是 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `Never`), ` 类型的 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `View`), ` 类型。由于这类 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `View`), ` 类型拥有其`, React.createElement(MDXTag, {\n      name: \"strong\",\n      components: components,\n      parentName: \"p\"\n    }, `内建`), `的逻辑，他们也被成为`, React.createElement(MDXTag, {\n      name: \"strong\",\n      components: components,\n      parentName: \"p\"\n    }, `内建视图`), `。`), React.createElement(MDXTag, {\n      name: \"p\",\n      components: components\n    }, `在我上面展示的斐波那契数列的例子中， 因为 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `Text`), ` 的 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `body`), ` 是 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `Never`), ` 类型的，同时 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `Text`), ` 的内建逻辑不会将程序执行转交给其他实例，程序的递归将终止在 SwiftUI 执行到 Fibonacci `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `body`), ` 中的 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `Text`), ` 视图时。`), React.createElement(MDXTag, {\n      name: \"pre\",\n      components: components\n    }, React.createElement(MDXTag, {\n      name: \"code\",\n      components: components,\n      parentName: \"pre\",\n      props: {\n        \"className\": \"language-swift\"\n      }\n    }, `extension Text : View {\n\n    public typealias Body = Never\n\n}\n`)), React.createElement(MDXTag, {\n      name: \"h2\",\n      components: components\n    }, `其他语言结构`), React.createElement(MDXTag, {\n      name: \"p\",\n      components: components\n    }, `在前面的描述中，我将 SwiftUI 执行模式中递归结束的条件限缩到了：`), React.createElement(MDXTag, {\n      name: \"blockquote\",\n      components: components\n    }, React.createElement(MDXTag, {\n      name: \"p\",\n      components: components,\n      parentName: \"blockquote\"\n    }, `一个不会将程序执行转交给其他 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `View`), ` 实例的`, React.createElement(MDXTag, {\n      name: \"strong\",\n      components: components,\n      parentName: \"p\"\n    }, `内建`), `视图`)), React.createElement(MDXTag, {\n      name: \"p\",\n      components: components\n    }, `从这个描述中你也许可以推断，SwiftUI 中存在内建视图会将程序执行逻辑转交给其他 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `View`), ` 实例。没错，确实如此。`), React.createElement(MDXTag, {\n      name: \"p\",\n      components: components\n    }, `在这里我将介绍其中的一个以帮助大家理解上面斐波那契数列范例中 SwiftUI 所提供的语言结构：`, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `Fibonacci`), ` `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `body`), ` 中 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `if..else...`), ` 语句背后的语言结构 —— `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `_ConditionalContent`), `。`), React.createElement(MDXTag, {\n      name: \"pre\",\n      components: components\n    }, React.createElement(MDXTag, {\n      name: \"code\",\n      components: components,\n      parentName: \"pre\",\n      props: {\n        \"className\": \"language-swift\"\n      }\n    }, `struct Fibonacci: View {\n\n    var body: some View {\n        // \\`if...else...\\` 语句将最终声称一个 \n        // \\`_ConditionalContent\\` 实例\n        if ordinal == 0 {\n            // ...\n        } else {\n            // ...\n        }\n    }\n\n}\n`)), React.createElement(MDXTag, {\n      name: \"p\",\n      components: components\n    }, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `_ConditionalContent`), ` 并没有在苹果的公开文档中暴露出来. 但是我们可以在 SwiftUI 的 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `swiftinterface`), ` 文件中找到它：`), React.createElement(MDXTag, {\n      name: \"pre\",\n      components: components\n    }, React.createElement(MDXTag, {\n      name: \"code\",\n      components: components,\n      parentName: \"pre\",\n      props: {\n        \"className\": \"language-swift\"\n      }\n    }, `public struct _ConditionalContent<TrueContent, FalseContent> {\n\n  internal enum Storage {\n    case trueContent(TrueContent)\n    case falseContent(FalseContent)\n  }\n\n  internal let storage: Storage\n\n}\n\nextension ConditionalContent : View where TrueContent : View, FalseContent : View {\n\n  public typealias Body = Never\n\n  internal init(storage: Storage)\n\n}\n`)), React.createElement(MDXTag, {\n      name: \"blockquote\",\n      components: components\n    }, React.createElement(MDXTag, {\n      name: \"p\",\n      components: components,\n      parentName: \"blockquote\"\n    }, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `swiftinterface`), ` 文件之于 Swift module 就像头文件之于 clang module。`)), React.createElement(MDXTag, {\n      name: \"p\",\n      components: components\n    }, `同时，SwiftUI 的 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `ViewBuilder`), ` 拥有一个 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `extension`), `。里面的内容告诉我们，当我们在一个被 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `@ViewBuiler`), ` 标记的函数内使用 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `if...else...`), ` 语句时，Swift 编译器将会在编译时生成一个 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `_ConditionalContent`), ` 实例来包裹 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `if...else...`), ` 两侧分支内的内容 —— 当 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `if`), ` 后的表达式为 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `true`), ` 的时候，生成代码唤起第一个 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `buildEither`), ` 函数；为 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `false`), ` 时，生成代码唤起第二个 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `buildEither`), ` 函数。`), React.createElement(MDXTag, {\n      name: \"pre\",\n      components: components\n    }, React.createElement(MDXTag, {\n      name: \"code\",\n      components: components,\n      parentName: \"pre\",\n      props: {\n        \"className\": \"language-swift\"\n      }\n    }, `extension ViewBuilder {\n\n  public static func buildEither<TrueContent : View, FalseContent : View>(first: TrueContent) -> ConditionalContent<TrueContent, FalseContent> {\n    .init(storage: .trueContent(first))\n  }\n\n  public static func buildEither<TrueContent : View, FalseContent : View>(second: FalseContent) -> ConditionalContent<TrueContent, FalseContent> {\n   .init(storage: .falseContent(second))\n  }\n\n}\n`)), React.createElement(MDXTag, {\n      name: \"p\",\n      components: components\n    }, `通过将我们在 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `ViewBuilder`), ` `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `extension`), ` 中所发现的以及 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `_ConditionalContent`), ` 本身的设计相结合，我们而可以推导出 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `_ConditionalContent`), ` 能够携带两种实例 —— 一种是 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `if...else`), ` 的 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `ture`), ` 分支下的内容，一种是 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `false`), ` 分支下的内容。`), React.createElement(MDXTag, {\n      name: \"p\",\n      components: components\n    }, `举个例子，在斐波那契数列范例中，当 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `if`), ` 之后的表达式为 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `true`), ` 时，`, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `Fibonacci`), ` 的 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `body`), ` 等于如下代码：`), React.createElement(MDXTag, {\n      name: \"pre\",\n      components: components\n    }, React.createElement(MDXTag, {\n      name: \"code\",\n      components: components,\n      parentName: \"pre\",\n      props: {\n        \"className\": \"language-swift\"\n      }\n    }, `struct Fibonacci: View {\n\n    var body: some View {\n        _ConditionalContent(storage: .trueContent(Text(...)))\n    }\n\n}\n`)), React.createElement(MDXTag, {\n      name: \"p\",\n      components: components\n    }, `当表达式为 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `false`), ` 时，`, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `Fibonacci`), ` 的 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `body`), ` 等于如下代码：`), React.createElement(MDXTag, {\n      name: \"pre\",\n      components: components\n    }, React.createElement(MDXTag, {\n      name: \"code\",\n      components: components,\n      parentName: \"pre\",\n      props: {\n        \"className\": \"language-swift\"\n      }\n    }, `struct Fibonacci: View {\n\n    var body: some View {\n        _ConditionalContent(storage: .falseContent(Fibonacci(...)))\n    }\n\n}\n`)), React.createElement(MDXTag, {\n      name: \"p\",\n      components: components\n    }, React.createElement(MDXTag, {\n      name: \"strong\",\n      components: components,\n      parentName: \"p\"\n    }, `然后 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"strong\"\n    }, `_ConditionContent`), ` 的内建逻辑最终会将程序执行转交给它 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"strong\"\n    }, `storage`), ` 中存储的 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"strong\"\n    }, `View`), ` 实例`)), React.createElement(MDXTag, {\n      name: \"p\",\n      components: components\n    }, `所以我们可以知道，除非 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `Fibonacci`), ` 的 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `ordinal`), ` 为 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `0`), `，`, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `Fibonacci`), ` 的 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `body`), ` 将永远递归地生成另一个 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `Fibonacci`), `。然后当 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `Fibonacci`), ` 的 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `ordinal`), ` 为 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `0`), ` 时，`, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `Fibonacci`), ` 的 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `body`), ` 将生成一个 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `Text`), ` 视图以渲染 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"p\"\n    }, `Fibonacci`), ` 的最终输出结果。`), React.createElement(MDXTag, {\n      name: \"h2\",\n      components: components\n    }, `结论`), React.createElement(MDXTag, {\n      name: \"p\",\n      components: components\n    }, `通过之前的分析，我们可以清楚地知道 SwiftUI 确实为上述斐波那契数列范例的执行过程提供了关键的语言结构，他们包括：`), React.createElement(MDXTag, {\n      name: \"ul\",\n      components: components\n    }, React.createElement(MDXTag, {\n      name: \"li\",\n      components: components,\n      parentName: \"ul\"\n    }, `一个带来可递归执行模式的 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"li\"\n    }, `View`), ` 协议。`), React.createElement(MDXTag, {\n      name: \"li\",\n      components: components,\n      parentName: \"ul\"\n    }, `一个遵从 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"li\"\n    }, `View`), ` 协议的类型 `, React.createElement(MDXTag, {\n      name: \"inlineCode\",\n      components: components,\n      parentName: \"li\"\n    }, `_ConditionalContent`), `。它负责处理条件分支。`)), React.createElement(MDXTag, {\n      name: \"p\",\n      components: components\n    }, `这就很像一门真实的编程语言了。所以我们可以说相较是一个 UI 框架，SwiftUI 更像是一门语言。`), React.createElement(MDXTag, {\n      name: \"h2\",\n      components: components\n    }, `一个形式化的视角`), React.createElement(MDXTag, {\n      name: \"p\",\n      components: components\n    }, `实际上，这个斐波那契数列的范例展现了 SwiftUI 是图灵完备的：如果一个过程可以重新进入它自己，并且这个过程可以无限改变它的行为，那么这个过程背后的语言就是图灵完备的。`));\n  }\n\n}\nMDXContent.isMDXComponent = true;","scope":""},"headings":[{"value":"前言","depth":2},{"value":"View 协议中未被解释的部分","depth":2},{"value":"其他语言结构","depth":2},{"value":"结论","depth":2},{"value":"一个形式化的视角","depth":2}]}}},"earlierPostExcerpt":null,"laterPostExcerpt":{"slug":"/post/2022/08/using-functional-binding-to-observe-in-swiftui-19a8","title":"在 SwiftUI 中使用函数式 Binding 实现观察者模式","subtitle":"","createdTime":"2022-08-19T00:00:00.000Z","tags":["SwiftUI","Binding","Swift","观察者模式"],"category":"编程","file":{"childMdx":{"excerpt":"故事 这周，我的同事问了我一个问题：在 SwiftUI 中怎么观察用户对  Picker  的选择行为？ 这是一个来自真实业务的问题，所以我觉得值得我花费时间去解决它。 范例代码如下所示，然后我的同事想观察用户对  Picker  候选项的选择行为。 分析 但是，「观察」本身的意义可能会随着上下文变动而变动： 它可以表示用户在  Picker  上放下手指的那一刻。 它可以表示用户在  Picker  上抬起手指的那一刻。 它可以表示  Picker  对  $selection  进行值变更的那一刻。 上述每一项都将导致不同的最终解决方案。 因为 SwiftUI 控件可以使用 style…"}}}},"pageContext":{"postId":"5b9ea0ff-9d12-540c-a169-462414cddcb7","earlierPostId":null,"laterPostId":"5c75555f-38a8-50fe-b14c-454701f208be"}}