例如:
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?("Howdy!")
行做了什么?它会完全优化生产线吗?还是在运行时将表达式L.og
评估为nil
?我如何证明任一答案?
我制作了一个小程序,只包含您上面看到的代码,并使用debug
和release
配置对它进行了构建。然后,我用swift build -c
分解了两个二进制文件。正如预期的那样,在调试程序集中,我可以看到“ Howdy”消息。但是,我对程序集和macOS体系结构了解不足,不足以证明发行程序集中不存在“ Howdy”。
接下来,我将objdump
代码放入一个更大的iOS项目中,将方案Run操作Build Configuration设置设置为'Release',在L.og
行上设置一个断点,启动了应用程序,并在遇到断点时,我按了Xcode Debug导航器中的“ Step into”按钮,以进入L.og
行,并且正如预期的那样,Xcode / lldb调试器没有进入L.og
,但确实如此无法证明二进制文件的内容。
L.og
不讨论此技术细节。
假定一个典型的构建环境,其中仅在Swift docs on "Optional Chaining"配置上设置编译器标志-DDEBUG
,而在debug
配置上设置not。
使用条件编译,编译器将在构建时评估标志的状态。
在release
块内评估为true的代码被编译,并且在#if
块内评估为false的代码被忽略。 (它将被剥离,并且不会出现在二进制文件中。)对于您而言,对于发行版,就好像您的代码是:
#if
因此,struct L {
static let og:((String) -> Void)? = nil
}
L.og?("Howdy!")
print("Done.")
中的“可选链接”会将L.og?("Howdy!")
评估为nil,并且该代码将永远不会做任何事情。
我不知道您问题的最后一部分的答案,因为编译器永远无法做任何事情,因此编译器是否完全优化了这一行。生成Swift代码的LLVM编译器非常聪明,在发布模式下,它很可能会将代码完全剥离掉,但是我不确定。您必须确保查看程序集输出。
我正在阅读L.og
,并且我认为这有助于我找到问题的答案。我不知道如何从命令行快速编译中执行此操作,因此我在Xcode中使用最小的iOS项目完成了此操作。
我编辑了方案运行操作,内部配置设置为“发布”。我保留了[调试可执行程序]的复选框,以便Xcode可以将源代码与程序集相关联。我已验证这实际上是通过优化构建项目的。这是来自Xcode Report导航器的(截断的)build命令,它与修改后的方案的构建相对应。注意中间的this answer。
-O
这里是AppDelgate中的测试代码。
CompileSwift normal x86_64 (in target: Beyond)
cd /Users/ftal/Code/Beyond
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift -frontend -c /Users/ftal/Code/Beyond/Beyond/ViewController.swift /Users/ftal/Code/Beyond/Beyond/AppDelegate.swift /Users/ftal/Code/Beyond/Beyond/Log.swift -emit-module-path /Users/ftal/Library/Developer/Xcode/DerivedData/Beyond-hfvhdaxgxprsdyasgilratcpkqze/Build/Intermediates.noindex/Beyond.build/Release-iphonesimulator/Beyond.build/Objects-normal-tsan/x86_64/Beyond.swiftmodule -emit-module-doc-path /Users/ftal/Library/Developer/Xcode/DerivedData/Beyond-hfvhdaxgxprsdyasgilratcpkqze/Build/Intermediates.noindex/Beyond.build/Release-iphonesimulator/Beyond.build/Objects-normal-tsan/x86_64/Beyond.swiftdoc -serialize-diagnostics-path /Users/ftal/Library/Developer/Xcode/DerivedData/Beyond-hfvhdaxgxprsdyasgilratcpkqze/Build/Intermediates.noindex/Beyond.build/Release-iphonesimulator/Beyond.build/Objects-normal-tsan/x86_64/Beyond-master.dia -emit-objc-header-path /Users/ftal/Library/Developer/Xcode/DerivedData/Beyond-hfvhdaxgxprsdyasgilratcpkqze/Build/Intermediates.noindex/Beyond.build/Release-iphonesimulator/Beyond.build/Objects-normal-tsan/x86_64/Beyond-Swift.h -emit-dependencies-path /Users/ftal/Library/Developer/Xcode/DerivedData/Beyond-hfvhdaxgxprsdyasgilratcpkqze/Build/Intermediates.noindex/Beyond.build/Release-iphonesimulator/Beyond.build/Objects-normal-tsan/x86_64/Beyond-master.d -target x86_64-apple-ios12.2-simulator -enable-objc-interop -sdk /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator12.4.sdk -I /Users/ftal/Library/Developer/Xcode/DerivedData/Beyond-hfvhdaxgxprsdyasgilratcpkqze/Build/Products/Release-iphonesimulator -F /Users/ftal/Library/Developer/Xcode/DerivedData/Beyond-hfvhdaxgxprsdyasgilratcpkqze/Build/Products/Release-iphonesimulator -g -module-cache-path /Users/ftal/Library/Developer/Xcode/DerivedData/ModuleCache.noindex -sanitize=thread -swift-version 5 -enforce-exclusivity=checked -O -serialize-debugging-options -Xcc -working-directory -Xcc /Users/ftal/Code/Beyond -enable-anonymous-context-mangled-names -Xcc -I/Users/ftal/Library/Developer/Xcode/DerivedData/Beyond-hfvhdaxgxprsdyasgilratcpkqze/Build/Intermediates.noindex/Beyond.build/Release-iphonesimulator/Beyond.build/swift-overrides.hmap -Xcc -iquote -Xcc /Users/ftal/Library/Developer/Xcode/DerivedData/Beyond-hfvhdaxgxprsdyasgilratcpkqze/Build/Intermediates.noindex/Beyond.build/Release-iphonesimulator/Beyond.build/Beyond-generated-files.hmap -Xcc -I/Users/ftal/Library/Developer/Xcode/DerivedData/Beyond-hfvhdaxgxprsdyasgilratcpkqze/Build/Intermediates.noindex/Beyond.build/Release-iphonesimulator/Beyond.build/Beyond-own-target-headers.hmap -Xcc -I/Users/ftal/Library/Developer/Xcode/DerivedData/Beyond-hfvhdaxgxprsdyasgilratcpkqze/Build/Intermediates.noindex/Beyond.build/Release-iphonesimulator/Beyond.build/Beyond-all-target-headers.hmap -Xcc -iquote -Xcc /Users/ftal/Library/Developer/Xcode/DerivedData/Beyond-hfvhdaxgxprsdyasgilratcpkqze/Build/Intermediates.noindex/Beyond.build/Release-iphonesimulator/Beyond.build/Beyond-project-headers.hmap -Xcc -I/Users/ftal/Library/Developer/Xcode/DerivedData/Beyond-hfvhdaxgxprsdyasgilratcpkqze/Build/Products/Release-iphonesimulator/include -Xcc -I/Users/ftal/Library/Developer/Xcode/DerivedData/Beyond-hfvhdaxgxprsdyasgilratcpkqze/Build/Intermediates.noindex/Beyond.build/Release-iphonesimulator/Beyond.build/DerivedSources-normal/x86_64 -Xcc -I/Users/ftal/Library/Developer/Xcode/DerivedData/Beyond-hfvhdaxgxprsdyasgilratcpkqze/Build/Intermediates.noindex/Beyond.build/Release-iphonesimulator/Beyond.build/DerivedSources/x86_64 -Xcc -I/Users/ftal/Library/Developer/Xcode/DerivedData/Beyond-hfvhdaxgxprsdyasgilratcpkqze/Build/Intermediates.noindex/Beyond.build/Release-iphonesimulator/Beyond.build/DerivedSources -module-name Beyond -num-threads 12 -o /Users/ftal/Library/Developer/Xcode/DerivedData/Beyond-hfvhdaxgxprsdyasgilratcpkqze/Build/Intermediates.noindex/Beyond.build/Release-iphonesimulator/Beyond.build/Objects-normal-tsan/x86_64/ViewController.o -o /Users/ftal/Library/Developer/Xcode/DerivedData/Beyond-hfvhdaxgxprsdyasgilratcpkqze/Build/Intermediates.noindex/Beyond.build/Release-iphonesimulator/Beyond.build/Objects-normal-tsan/x86_64/AppDelegate.o -o /Users/ftal/Library/Developer/Xcode/DerivedData/Beyond-hfvhdaxgxprsdyasgilratcpkqze/Build/Intermediates.noindex/Beyond.build/Release-iphonesimulator/Beyond.build/Objects-normal-tsan/x86_64/Log.o
我在func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { print("Before") L.og?("Howdy!") print("After") return true }
处设置了一个断点。我运行了这个项目。遇到断点时,我在return true
提示符下输入了dis -f -m
。
lldb
我很确定我在Before After Beyond was compiled with optimization - stepping may behave oddly; variables may not be available. (lldb) dis -f -m ** 15 func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { Beyond`specialized AppDelegate.application(_:didFinishLaunchingWithOptions:): 0x103b2aba0 <+0>: pushq %rbp 0x103b2aba1 <+1>: movq %rsp, %rbp 0x103b2aba4 <+4>: pushq %r15 0x103b2aba6 <+6>: pushq %r14 0x103b2aba8 <+8>: pushq %r13 0x103b2abaa <+10>: pushq %r12 0x103b2abac <+12>: pushq %rbx 0x103b2abad <+13>: pushq %rax 0x103b2abae <+14>: movq 0x8(%rbp), %rdi 0x103b2abb2 <+18>: callq 0x103b2af38 ; symbol stub for: __tsan_func_entry 0x103b2abb7 <+23>: movq 0x251a(%rip), %rcx ; (void *)0x0000000105fa0b68: type metadata for Any 0x103b2abbe <+30>: addq $0x8, %rcx 0x103b2abc2 <+34>: leaq 0x37c7(%rip), %rsi ; lazy cache variable for type metadata for Swift._ContiguousArrayStorage<Any> 0x103b2abc9 <+41>: movq 0x24f0(%rip), %r8 ; (void *)0x0000000105e64e70: type metadata accessor for Swift._ContiguousArrayStorage 0x103b2abd0 <+48>: xorl %edi, %edi 0x103b2abd2 <+50>: movq %rsi, %rdx 0x103b2abd5 <+53>: callq 0x103b2adc0 ; merged type metadata accessor for __C.UIApplicationLaunchOptionsKey 0x103b2abda <+58>: movq %rax, %r14 0x103b2abdd <+61>: movl $0x40, %esi 0x103b2abe2 <+66>: movl $0x7, %edx 0x103b2abe7 <+71>: movq %rax, %rdi 0x103b2abea <+74>: callq 0x103b2af92 ; symbol stub for: swift_allocObject 0x103b2abef <+79>: movq %rax, %rbx 0x103b2abf2 <+82>: leaq 0x10(%rax), %rdi 0x103b2abf6 <+86>: callq 0x103b2af5c ; symbol stub for: __tsan_write16 0x103b2abfb <+91>: movaps 0x1dbe(%rip), %xmm0 ; __swift_reflection_version + 16 0x103b2ac02 <+98>: movups %xmm0, 0x10(%rbx) 0x103b2ac06 <+102>: leaq 0x20(%rbx), %r15 ** 16 print("Before") 0x103b2ac0a <+106>: leaq 0x38(%rbx), %rdi 0x103b2ac0e <+110>: callq 0x103b2af62 ; symbol stub for: __tsan_write8 0x103b2ac13 <+115>: movq 0x243e(%rip), %r12 ; (void *)0x0000000105f99190: type metadata for Swift.String 0x103b2ac1a <+122>: movq %r12, 0x38(%rbx) 0x103b2ac1e <+126>: movq %r15, %rdi 0x103b2ac21 <+129>: callq 0x103b2af62 ; symbol stub for: __tsan_write8 0x103b2ac26 <+134>: movabsq $0x65726f666542, %rax ; imm = 0x65726F666542 0x103b2ac30 <+144>: movq %rax, 0x20(%rbx) 0x103b2ac34 <+148>: leaq 0x28(%rbx), %rdi 0x103b2ac38 <+152>: callq 0x103b2af62 ; symbol stub for: __tsan_write8 0x103b2ac3d <+157>: movabsq $-0x1a00000000000000, %rax ; imm = 0xE600000000000000 0x103b2ac47 <+167>: movq %rax, 0x28(%rbx) 0x103b2ac4b <+171>: movabsq $-0x1f00000000000000, %rdx ; imm = 0xE100000000000000 0x103b2ac55 <+181>: movl $0x20, %esi 0x103b2ac5a <+186>: movl $0xa, %ecx 0x103b2ac5f <+191>: movq %rbx, %rdi 0x103b2ac62 <+194>: movq %rdx, %r8 0x103b2ac65 <+197>: callq 0x103b2af08 ; symbol stub for: Swift.print(_: Any..., separator: Swift.String, terminator: Swift.String) -> () 0x103b2ac6a <+202>: movq %rbx, %rdi 0x103b2ac6d <+205>: callq 0x103b2afbc ; symbol stub for: swift_release 11 static let og:((String) -> Void)? = { print($0) } 12 #else ** 13 static let og:((String) -> Void)? = nil 14 #endif 15 } 0x103b2ac72 <+210>: leaq 0x3677(%rip), %rdi ; globalinit_33_3570E65C86025A8C4F03B9A75B67FFAA_token0 0x103b2ac79 <+217>: callq 0x103b2af56 ; symbol stub for: __tsan_read8 0x103b2ac7e <+222>: cmpq $-0x1, 0x366a(%rip) ; (void *)0x0000000000000200 0x103b2ac86 <+230>: je 0x103b2aca7 ; <+263> at AppDelegate.swift:17:11 0x103b2ac88 <+232>: leaq 0x3661(%rip), %rdi ; globalinit_33_3570E65C86025A8C4F03B9A75B67FFAA_token0 0x103b2ac8f <+239>: leaq 0x1fa(%rip), %rsi ; globalinit_33_3570E65C86025A8C4F03B9A75B67FFAA_func0 at Log.swift 0x103b2ac96 <+246>: callq 0x103b2afb6 ; symbol stub for: swift_once 0x103b2ac9b <+251>: leaq 0x364e(%rip), %rdi ; globalinit_33_3570E65C86025A8C4F03B9A75B67FFAA_token0 0x103b2aca2 <+258>: callq 0x103b2af56 ; symbol stub for: __tsan_read8 ** 17 L.og?("Howdy!") 0x103b2aca7 <+263>: leaq 0x39f2(%rip), %rdi ; static Beyond.L.og : Swift.Optional<(Swift.String) -> ()> 0x103b2acae <+270>: callq 0x103b2af56 ; symbol stub for: __tsan_read8 0x103b2acb3 <+275>: movq 0x39e6(%rip), %rbx ; static Beyond.L.og : Swift.Optional<(Swift.String) -> ()> 0x103b2acba <+282>: leaq 0x39e7(%rip), %rdi ; static Beyond.L.og : Swift.Optional<(Swift.String) -> ()> + 8 0x103b2acc1 <+289>: callq 0x103b2af56 ; symbol stub for: __tsan_read8 0x103b2acc6 <+294>: testq %rbx, %rbx 0x103b2acc9 <+297>: je 0x103b2acfe ; <+350> [inlined] generic specialization <Any> of Swift._allocateUninitializedArray<A>(Builtin.Word) -> (Swift.Array<A>, Builtin.RawPointer) at AppDelegate.swift:18 1 // 1 // 2 // AppDelegate.swift 0x103b2accb <+299>: movq 0x39d6(%rip), %r15 ; static Beyond.L.og : Swift.Optional<(Swift.String) -> ()> + 8 0x103b2acd2 <+306>: movq %r15, %rdi 0x103b2acd5 <+309>: callq 0x103b2afc2 ; symbol stub for: swift_retain 0x103b2acda <+314>: movabsq $0x217964776f48, %rdi ; imm = 0x217964776F48 0x103b2ace4 <+324>: movabsq $-0x1a00000000000000, %rsi ; imm = 0xE600000000000000 ** 17 L.og?("Howdy!") 0x103b2acee <+334>: movq %r15, %r13 0x103b2acf1 <+337>: callq *%rbx 0x103b2acf3 <+339>: movq %rbx, %rdi 0x103b2acf6 <+342>: movq %r15, %rsi 0x103b2acf9 <+345>: callq 0x103b2ae50 ; outlined consume of Swift.Optional<@escaping @callee_guaranteed (@guaranteed Swift.String) -> ()> 0x103b2acfe <+350>: movl $0x40, %esi 0x103b2ad03 <+355>: movl $0x7, %edx 0x103b2ad08 <+360>: movq %r14, %rdi 0x103b2ad0b <+363>: callq 0x103b2af92 ; symbol stub for: swift_allocObject 0x103b2ad10 <+368>: movq %rax, %rbx 0x103b2ad13 <+371>: leaq 0x10(%rax), %rdi 0x103b2ad17 <+375>: callq 0x103b2af5c ; symbol stub for: __tsan_write16 0x103b2ad1c <+380>: movaps 0x1c9d(%rip), %xmm0 ; __swift_reflection_version + 16 0x103b2ad23 <+387>: movups %xmm0, 0x10(%rbx) 0x103b2ad27 <+391>: leaq 0x20(%rbx), %r14 ** 18 print("After") 0x103b2ad2b <+395>: leaq 0x38(%rbx), %rdi 0x103b2ad2f <+399>: callq 0x103b2af62 ; symbol stub for: __tsan_write8 0x103b2ad34 <+404>: movq %r12, 0x38(%rbx) 0x103b2ad38 <+408>: movq %r14, %rdi 0x103b2ad3b <+411>: callq 0x103b2af62 ; symbol stub for: __tsan_write8 0x103b2ad40 <+416>: movabsq $0x7265746641, %rax ; imm = 0x7265746641 0x103b2ad4a <+426>: movq %rax, 0x20(%rbx) 0x103b2ad4e <+430>: leaq 0x28(%rbx), %rdi 0x103b2ad52 <+434>: callq 0x103b2af62 ; symbol stub for: __tsan_write8 0x103b2ad57 <+439>: movabsq $-0x1b00000000000000, %rax ; imm = 0xE500000000000000 0x103b2ad61 <+449>: movq %rax, 0x28(%rbx) 0x103b2ad65 <+453>: movabsq $-0x1f00000000000000, %rdx ; imm = 0xE100000000000000 0x103b2ad6f <+463>: movl $0x20, %esi 0x103b2ad74 <+468>: movl $0xa, %ecx 0x103b2ad79 <+473>: movq %rbx, %rdi 0x103b2ad7c <+476>: movq %rdx, %r8 0x103b2ad7f <+479>: callq 0x103b2af08 ; symbol stub for: Swift.print(_: Any..., separator: Swift.String, terminator: Swift.String) -> () 0x103b2ad84 <+484>: movq %rbx, %rdi 0x103b2ad87 <+487>: callq 0x103b2afbc ; symbol stub for: swift_release -> 19 return true -> 20 } -> 21 } -> 0x103b2ad8c <+492>: callq 0x103b2af3e ; symbol stub for: __tsan_func_exit 0x103b2ad91 <+497>: movb $0x1, %al 0x103b2ad93 <+499>: addq $0x8, %rsp 0x103b2ad97 <+503>: popq %rbx 0x103b2ad98 <+504>: popq %r12 0x103b2ad9a <+506>: popq %r13 0x103b2ad9c <+508>: popq %r14 0x103b2ad9e <+510>: popq %r15 0x103b2ada0 <+512>: popq %rbp 0x103b2ada1 <+513>: retq 1 // 0x103b2ada2 <+514>: movq %rax, %rbx 0x103b2ada5 <+517>: callq 0x103b2af3e ; symbol stub for: __tsan_func_exit 0x103b2adaa <+522>: movq %rbx, %rdi 0x103b2adad <+525>: callq 0x103b2af26 ; symbol stub for: _Unwind_Resume 0x103b2adb2 <+530>: ud2 0x103b2adb4 <+532>: nopw %cs:(%rax,%rax) 0x103b2adbe <+542>: nop (lldb)
和** 13
两行中看到的输出都表明,即使由于条件编译块而导致该可选项始终为** 17
时,编译器也无法优化该可选项。