【问题标题】:Are the #if DEBUG statements really needed for previews in SwiftUI to remove it in a release build?SwiftUI 中的预览是否真的需要 #if DEBUG 语句才能在发布版本中删除它?
【发布时间】:2019-10-22 10:39:34
【问题描述】:

预处理器宏在 SwiftUI 官方教程/视频中很常见,例如:

#if DEBUG
struct ContentView_Previews : PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
#endif

那些需要吗?编译器可以肯定地看到 struct 没有在内部使用并省略整个 struct 因为访问修饰符是隐式的 internal 对吗?我认为所有符合PreviewProvider 的东西都可以去掉,但也许不是每个符合条件的对象都是这样,但如果不使用,为什么Apple 决定包含预处理器宏?

我尝试在发布模式下运行它并在派生数据文件夹中找到已编译的类,但我对它一无所知(.o 文件)。 任何人都可以确认我们是否真的需要在发布版本中包含宏以省略未使用的代码(ContentView_Previews 类型不会在代码中的任何地方使用,期望预览在发布版本中无论如何都不会使用)?

【问题讨论】:

    标签: swift c-preprocessor swiftui


    【解决方案1】:

    您不需要删除它。 DEBUG 是自动添加到Your Target -> Build Settings -> Active compilation Conditions 的自定义标志。此标志仅存在于 Debug 配置中,对于发布版本,您将使用没有此标志的 Release 配置。

    如果标志不存在 - 代码将被省略

    Read more here 在“调试预处理器宏”部分中

    【讨论】:

    • 首先你说:“你不需要删除它”,然后:“如果标志不存在 - 代码将被省略”所以我不确定标志是否'不在那里,代码将被省略。您有任何参考资料可以支持您的想法吗?
    • 我认为你误解了这个问题。 OP 询问在这种情况下是否真的需要 if DEBUG endif 预处理器。关键是预览代码显然是调试代码,所以我们是否需要将其显式标记为调试代码,或者编译器是否足够聪明,可以在没有宏的情况下将其从输出构建中省略?
    • 你是对的。我错过了核心问题。但是我相信如果我们删除这个#if,它里面的代码即使对于Release配置也会被编译,但不会被使用。结果它会增加应用程序的大小(或其他东西)。它类似于@IBDesignable 行为(因此Apple 可以将#if #endif 替换为@PreviewProvider 修饰符。
    • Xcode 11.2.0 要求您删除 ContentView_Previews: PreviewProvider 周围的#if DEBUG 标志
    【解决方案2】:

    它似乎已从 11 GM 种子中删除。 GM 种子发行说明说(在已解决的问题下):

    “围绕 PreviewProvider 类型的 #if/#endif 编译器条件已从 SwiftUI 模板中删除。存档时,预览提供程序未从已构建的产品中正确删除。(51539802)”

    不确定这是否意味着预览提供程序当前未被删除,或者他们修复了问题并现在将其删除。我想如果模板代码删除了 #if 这意味着 Apple 认为它不再需要。

    【讨论】:

    • 我刚刚归档了一个 SwiftUI 应用程序,并且必须在 PreviewProviders 周围添加编译器条件才能使其工作。这可能是因为我在 PreviewProviders 引用的编译器条件中使用了模拟数据。
    【解决方案3】:

    Xcode 11.2 及更高版本不需要#if DEBUG,因此,您将执行以下操作并删除 PreviewProvider 周围的#if DEBUG

    struct ContentView: View {
        var body: some View {
            NavigationView {
                List {
                    Text("First Row")
                    Text("First Row")
                    Text("First Row")
                    Text("First Row")
                }.navigationBarTitle(Text("Dynamic List"))
            }
        }
    
    }
    
    
    
    struct ContentView_Previews: PreviewProvider {
        static var previews: some View {
            ContentView()
        }
    }
    

    【讨论】:

    • 您基于此答案的任何资源?
    • 这里有一个线程:forums.swift.org/t/… 谈论剥离 PreviewProvider 子类
    • @Arunabh Das,使用最新的 Xcode 和 iOS 版本在 SwiftUI 上工作,这是否意味着现在调试和发布模式都需要PreviewProvider
    【解决方案4】:

    注意:为了更加清楚,您不要需要将预览提供程序包装在 #if DEBUG 条件中。它们已从您的生产版本中删除。

    我有点晚了,但我只需要记下这一点,因为网络上到处都是混乱,而且结果很可笑。发行说明位于“Resolved Issues”下,已解决问题的标题为“PreviewProviders 在归档时未从构建的产品中正确删除。(51539802)” .

    是的,现在一切都说得通了。

    The still The proof

    Annnd Just in case you think they may have changed it later..... more proof

    (我很彻底……也许太过分了)

    【讨论】:

    • 如果我使用 PreviewContent 文件夹中的代码,我必须添加 DEBUG 条件。
    【解决方案5】:

    对于您的特定情况,您可以删除 #if DEBUG 宏,但是当您想使用 #if DEBUG 宏中的一些模拟时,问题就来了。 Release 的构建将失败,因为看起来 Xcode 仍会尝试构建 PreviewProvider,很可能是在构建之后,它会从 Release 工件中剥离或取消链接代码。

    #if DEBUG
    
    class MyServiceMock: ServiceMockType {
        ...
    }
    
    #endif
    
    // Will fail when you try to release.
    
    struct ContentView_Previews : PreviewProvider {
        static var previews: some View {
            ContentView(service: MyServiceMock())
        }
    }
    

    这适用于测试和预览,但在您尝试发布时会失败。

    尽管它们不是必需的,但如果您使用仅为Debug 声明的任何代码,则需要它们。如果您在预览中使用的代码仅可用于调试,我的建议是保留它们。

    #if DEBUG
    
    class MyServiceMock: ServiceMockType {
        ...
    }
    
    #endif
    
    ...
    
    #if DEBUG
    struct ContentView_Previews : PreviewProvider {
        static var previews: some View {
            ContentView(service: MyServiceMock())
        }
    }
    #endif
    

    【讨论】:

    • 我已经看到了这个确切的问题,所以谢谢你的回答。
    猜你喜欢
    • 2013-07-29
    • 1970-01-01
    • 2018-04-19
    • 2014-09-04
    • 2021-11-25
    • 2014-12-24
    • 1970-01-01
    • 1970-01-01
    • 2014-06-01
    相关资源
    最近更新 更多