Swift 仅在条件编译块中设置可选,编译器是否从发布版本中完全删除语句?

Swift 仅在条件编译块中设置可选,编译器是否从发布版本中完全删除语句?,swift,assembly,build,Swift,Assembly,Build,例如: struct L { #if DEBUG static let og:((String) -> Void)? = { print($0) } #else static let og:((String) -> Void)? = nil #endif } L.og?("Howdy!") print("Done.") 在这段代码的发布版本中,Swift编译器如何处理L.og?(“你好!”)?它是否完全

例如:

struct L {
    #if DEBUG
    static let og:((String) -> Void)? = { print($0) }
    #else
    static let og:((String) -> Void)? = nil
    #endif
}
L.og?("Howdy!")
print("Done.")

在这段代码的发布版本中,Swift编译器如何处理
L.og?(“你好!”)
?它是否完全优化了生产线?或者表达式
L.og
在运行时计算为
nil
?如何证明任一答案?

使用条件编译,编译器在生成时评估标志的状态

编译计算结果为true的#if块内的代码,忽略计算结果为false的#if块内的代码。(它被剥离,不会以二进制形式出现。)在您的情况下,对于发布版本,您的代码似乎是:

struct L {
    static let og:((String) -> Void)? = nil
}
L.og?("Howdy!")
print("Done.")
因此,
L.og?(“Howdy!”)
中的“可选链接”将
L.og
计算为nil,并且该代码永远不会做任何事情


我不知道你问题的最后部分的答案,关于编译器是否完全优化了行,因为它永远不会做任何事情。构建Swift代码的LLVM编译器非常智能,在发布模式下,它可能会完全删除代码,但我不确定。您必须查看汇编输出才能确定。

我想我终于回答了自己的问题。这个编辑完全取代了我以前的答案,因为我认为它是错误的

简短回答 编译器优化不会删除
Release
builds中的
L.og?(“Howdy!”)
行。
og?
的展开仍在运行时发生

证明 定义“发布” 在典型的Xcode项目中,
版本的优化是什么?在目标构建设置中,它是
SWIFT\u OPTIMIZATION\u LEVEL=-O

方法 我在两个不同的程序上运行了
swiftc-emit assembly-O

方案A:

struct L {
    #if DEBUG
    static let og:((String) -> Void)? = { print($0) }
    #else
    static let og:((String) -> Void)? = nil
    #endif
}
L.og?("Howdy!")
方案B:

struct L {
    #if DEBUG
    static let og:((String) -> Void)? = { print($0) }
    #else
    static let og:((String) -> Void)? = nil
    #endif
}
在程序集输出中,来自

B:

如果编译器优化掉了整行
L.og?(“Howdy!”)
,那么我希望B的
main
与A相同。事实并非如此。因此,编译器将其保留在

非可选结果 对于非可选的空闭包,会发现类似的结果。对
L.og(“Howdy!”)
的调用保留在优化程序集中,即使调用的是空函数

即:

struct L {
    #if DEBUG
    static let og:((String) -> Void) = { print($0) }
    #else
    static let og:((String) -> Void) = { _ in }
    #endif
}
L.og("Howdy!")
这里是优化的
main
assembly:

_main:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register %rbp
    pushq   %r13
    pushq   %rax
    .cfi_offset %r13, -24
    cmpq    $-1, _globalinit_029_12232F587A4C5CD8B1EEDF696793B2FC_token0(%rip)
    jne LBB0_1
LBB0_2:
    movq    _$s4main1LV2ogyySScvpZ+8(%rip), %r13
    movabsq $36805260308296, %rdi
    movabsq $-1873497444986126336, %rsi
    callq   *_$s4main1LV2ogyySScvpZ(%rip)
    xorl    %eax, %eax
    addq    $8, %rsp
    popq    %r13
    popq    %rbp
    retq
LBB0_1:
    leaq    _globalinit_029_12232F587A4C5CD8B1EEDF696793B2FC_token0(%rip), %rdi
    leaq    _globalinit_029_12232F587A4C5CD8B1EEDF696793B2FC_func0(%rip), %rsi
    callq   _swift_once
    jmp LBB0_2
    .cfi_endproc

    .p2align    4, 0x90

除非不能保证这是基于此
debug
标志的调试生成。换句话说:由于
DEBUG
标志对Swift没有特殊意义,它可能会出现在发布版本中(错误)。感谢您的响应,但您所写的是理解我的问题的先决知识。编译器所做的不是我问题的“最后一部分”,而是我在标题中所写的主要问题。我意识到,当我完成我的“答案”时。我想事后来看,作为一个评论会更好,但是注释上缺少格式将使其难以阅读。顺便说一句,您可以通过简单地使用无运算闭包而不是
nil
@Alexander来避免这里的可选性,但在这种情况下,我怀疑编译器优化行的机会会下降。我认为无论是哪种情况,它都会进入行。编译器会消除死代码,应该会清理掉。“在任何一种情况下,我认为这都应该是次要的,良好的API设计应该优先考虑,”IMO.@Alexander写道,“编译器会消除死代码,它应该清理掉它”。我的问题是如何证明这一点。谢谢你的帮助,但这个问题不是关于API设计的。如果我知道的话,我会回答的。因此,我留下了一条评论:)
struct L {
    #if DEBUG
    static let og:((String) -> Void) = { print($0) }
    #else
    static let og:((String) -> Void) = { _ in }
    #endif
}
L.og("Howdy!")
_main:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register %rbp
    pushq   %r13
    pushq   %rax
    .cfi_offset %r13, -24
    cmpq    $-1, _globalinit_029_12232F587A4C5CD8B1EEDF696793B2FC_token0(%rip)
    jne LBB0_1
LBB0_2:
    movq    _$s4main1LV2ogyySScvpZ+8(%rip), %r13
    movabsq $36805260308296, %rdi
    movabsq $-1873497444986126336, %rsi
    callq   *_$s4main1LV2ogyySScvpZ(%rip)
    xorl    %eax, %eax
    addq    $8, %rsp
    popq    %r13
    popq    %rbp
    retq
LBB0_1:
    leaq    _globalinit_029_12232F587A4C5CD8B1EEDF696793B2FC_token0(%rip), %rdi
    leaq    _globalinit_029_12232F587A4C5CD8B1EEDF696793B2FC_func0(%rip), %rsi
    callq   _swift_once
    jmp LBB0_2
    .cfi_endproc

    .p2align    4, 0x90