美文网首页iOS 开发每天分享优质文章SwiftUI
在SwiftUI的声明语法的编译器魔术中

在SwiftUI的声明语法的编译器魔术中

作者: iOS_小久 | 来源:发表于2019-06-14 20:36 被阅读46次

    SwiftUI在WWDC 2019年宣布,是一个令人难以置信的UI构建框架,可能永远改变iOS应用程序的制作方式。多年来,我们通过Storyboard或View Code参与了写视图的战争,而SwiftUI似乎最终以此结束。随着它的发布,不仅故事板现在几乎无关紧要,但老式的View Code也受到很大威胁,因为SwiftUI融合了两者的优点。

    使用这个新框架,您可以使用Xcode直观地定义屏幕,就像在Storyboard中一样 - 但不是生成不可读的XML文件,而是生成代码:

    小编推荐一个群 691040931 里面有许多的iOS开发者在交流技术分享自己的心得,更有一些资料不定期的分享更新。

    如果这还不够疯狂,对代码的更改将实时更新实时预览,为Xcode带来长期请求的功能。

    但最让我感兴趣的部分是,如果你看一下SwiftUI的例子,你会发现在当前的Swift中几乎没有任何意义 - 一堆看似断开的View属性怎么会导致完整的屏幕?

    struct LandmarkList: View {
    

    对声明性编程范例(你描述你想要的而不是显式编码)的强烈支持始终是Swift的目标,而SwiftUI的发布终于应用了这个概念。然而,斯威夫特不具备这些功能尚未 ; 上一个例子的工作原理是SwiftUI由大量的编译器功能提供支持 - 其中一些是Swift 5.1,还有一些还不是Swift的正式部分。一如既往,我调查了这一点。

    无回报的单个表达式

    您可能已经注意到虽然body返回a View,但没有return声明!Swift 5.1引入了无返回单个表达式,其中允许仅由一个表达式组成的闭包return为了可视目的而省略该语句。它的工作方式是你所期望的:当编译器解析一个函数体并注意到它只有一个语句时,它会return在树中注入一个标记。在这里查看。

    auto RS = new (Context) ReturnStmt(SourceLoc(), E);
    

    虽然我个人认为这可能有点令人困惑(但我没有太多使用它,所以我可能会改变主意),将声明性的感觉带入语言真的很棒。

    财产代表

    SwiftUI最有趣的功能之一是如何更改视图状态可以触发完整的UI重新加载。这是因为Xcode 11添加了Combine框架,正式以声明性Swift API的形式将大量的Reactive概念引入iOS。然而,更酷的不是这些概念本身,而是如何应用它们。上一个示例包含以下行:

    @State var showFavoritesOnly = false
    

    由于此属性标有属性,因此更改它会触发,从而导致在屏幕中绘制新的属性。@State``body``View

    此属性在Swift本身中不可用,但它与当前正在讨论的编译器功能正式添加到语言中:属性委托。

    有时,我们希望向一个属性添加更复杂的逻辑片段,而这个属性并不能证明使用新类型是合理的,至少在该范围内不是这样。这可以与属性观察者一样,如经典示例中所示:get/set/willSet/didSet``UserDefaults

    var isFirstBoot: Bool {
    

    这个例子很简单,但是如果你做一些更复杂的事情,比如手动实现lazy逻辑,不难看出这会变得多么臃肿:

    private var _foo: Int?
    

    在当前的Swift中,你唯一的选择是将它包装在一个泛型类型中,它在视觉上不适合SwiftUI的声明性编程风格。作为回应,Property Delegates提议是在2019年4月创建的。此编译器功能尚未正式添加到该语言中,但它已被用作SwiftUI的一部分。它的目的是完成我们在当前Swift中必须做的事情,但是从用户那里直观地抽象它Lazy<T>

    在创建泛型类型时,您现在可以将该@propertyDelegate属性添加到其声明中,以使其可用作属性:

    @propertyDelegate class UserDefault<T> {
    

    更新:似乎该属性的官方Swift 5.1名称将是@propertyWrapper,但SwiftUI beta正在使用,@propertyDelegate所以我正在使用这个。

    我现在可以使用isFirstBoot如下:

    @UserDefault(key: "isFirstBoot", defaultValue: false) var isFirstBoot: Bool
    

    这允许您与复杂的泛型类型接口,但在视觉上只看到常规Bool属性,如上所述,该属性使编译器魔术能够抽象原始泛型类型的创建。因为它的完整实现值得一篇自己的文章,我们将跳转到有趣的部分 - 使用属性委托,编译器将之前的声明转换为:How 'x' Works Internally in Swift

    var $isFirstBoot: UserDefaults<Bool> = UserDefaults<Bool>(key: "isFirstBoot", defaultValue: false)
    

    使用$前缀是故意的; 即使isFirstBoot是a Bool,我可能仍然希望从更复杂的泛型类型访问属性和方法。虽然隐藏了原始属性,但您仍可以为此目的访问它。例如,这是一个示例,其中UserDefault有一个方法将其返回到默认值:

    extension UserDefault {
    

    虽然Bool不具备此属性,但我可以使用它$isFirstBoot

    $isFirstBoot.reset()
    

    这就是为什么在SwiftUI的例子中使用:它不需要实际的,但是属性委托结构提供的属性能够在它改变时触发视图重新加载。Toggle(isOn: $showFavoritesOnly)``$``Bool``Binding``State

    /// A linked View property that instantiates a persistent state
    

    功能建设者

    我们已经看到单个表达式不需要添加return语句,但这到底是怎么回事?

    HStack {
    

    这将产生一个带有三个标签的漂亮水平堆栈,但我们所做的只是实例化它们!怎么可以添加到视图中?

    答案可能是SwiftUI中最具开创性的变化 - 功能构建器。

    在SwiftUI发布之后,函数构建器功能音调被引入Swift社区,允许Swift将工厂模式抽象为一个干净的可视化声明表达式。All表示它很快就会成为Swift本身的一部分,但是现在你可以尝试将其作为SwiftUI的一部分。

    函数构造器与给定闭包的类型相关,可以检索一系列语句并根据它们抽象创建更具体的东西。

    HStack可以这样做,因为它的ViewBuilder签名中有功能构建器:

    public init(..., content: @ViewBuilder () -> Content)
    

    @ViewBuilder转换为ViewBuilderstruct:一个函数构建器,可以将视图表达式转换为实际视图。函数构建器由@_functionBuilder属性决定(带下划线,因为我们不应该手动使用它)和一系列确定如何解析表达式的方法:

    @_functionBuilder public struct ViewBuilder {
    

    我们并不真正知道这些方法中发生了什么,因为它们是SwiftUI内部的,但我们知道它背后的编译器魔力。该示例将导致以下结果:

    HStack {
    

    块越复杂,魔术表达就越复杂。这里重要的是函数构建器块只能包含构建器可理解的内容。例如,您只能添加if语句,HStack因为它包含属性中的相关方法。如果您想要一些不起作用的示例,请尝试以下操作:buildIf()

    HStack {
    

    此功能的目的是在Swift中创建嵌入式DSL - 允许您定义转换为其他内容的内容,但它在向Swift提供声明性编程感觉方面起着重要作用。以下是使用此函数构建器构建HTML页面的方法:

    div {
    

    Xcode 11中的功能版本是内部版本,功能少于建议的Swift版本,因此在正式添加到语言中之前不应手动使用。


    到最后小编推荐一个群 691040931 里面有许多的iOS开发者在交流技术分享自己的心得,更有一些资料不定期的分享更新。
    资料截图

    转载地址:https://swiftrocks.com/inside-swiftui-compiler-magic.html

    相关文章

      网友评论

        本文标题:在SwiftUI的声明语法的编译器魔术中

        本文链接:https://www.haomeiwen.com/subject/zxdmfctx.html