代码之家  ›  专栏  ›  技术社区  ›  Mark A. Donohoe

swift编译器/链接器是否会自动删除未使用的方法/类/扩展等。?

  •  3
  • Mark A. Donohoe  · 技术社区  · 6 年前

    我们有很多代码可以在我们编写的任何iOS应用程序中使用。例如:

    • 自定义/通用控件
    • 对常见对象(如UIView、UIImage和UIViewController)的扩展
    • 全局效用函数
    • 全局常数

    由于与此问题无关的原因,我们不能使用静态或动态库。这些文件必须作为实际的源文件包含在项目中。

    问题是这样做会变得相当乏味,尤其是当有一组相关文件都引用了其他文件时。一个接一个的追踪他们真的很痛苦!

    我想知道的是,我是否可以简单地将所有内容都包含在目标中,然后依靠编译器删除所有未使用的代码。

    例如,我在UIView上有几个扩展方法。如果我不在特定的目标中使用它们,编译器会将该代码从编译的二进制文件中排除,还是会将其编译为不可访问的、无缘无故增大代码大小的文件?

    1 回复  |  直到 6 年前
        1
  •  6
  •   Hamish    6 年前

    死函数

    编译器的silooptimizer has a dead function elimination pass

    要充分利用这一点,您需要使用 whole module optimisation ( -wmo )为了确保编译器能够分析 internal 函数是否从同一模块中调用。

    您可以很容易地自己测试,例如使用以下代码:

    class C {}
    extension C {
      @inline(never) func foo() {}
      @inline(never) func bar() {}
    }
    
    @inline(never) func foo() {}
    @inline(never) func bar() {}
    bar()
    
    let c = C()
    c.bar()
    

    @inline(never) 以确保函数不会因内联而被优化)

    如果我们逃跑 xcrun swiftc -emit-sil -O -wmo main.swift | xcrun swift-demangle ,我们可以看到生成的SIL:

    sil_stage canonical
    
    import Builtin
    import Swift
    import SwiftShims
    
    class C {
      init()
      deinit
    }
    
    extension C {
      @inline(never) func foo()
      @inline(never) func bar()
    }
    
    @inline(never) func foo()
    
    @inline(never) func bar()
    
    let c: C
    
    // c
    sil_global hidden [let] @main.c : main.C : $C
    
    // main
    sil @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
    bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):
      // function_ref bar()
      %2 = function_ref @main.bar() -> () : $@convention(thin) () -> () // user: %3
      %3 = apply %2() : $@convention(thin) () -> ()
      alloc_global @main.c : main.C                  // id: %4
      %5 = global_addr @main.c : main.C : $*C        // user: %8
      %6 = alloc_ref $C                               // users: %8, %7
      debug_value %6 : $C, let, name "self", argno 1  // id: %7
      store %6 to %5 : $*C                            // id: %8
      // function_ref specialized C.bar()
      %9 = function_ref @function signature specialization <Arg[0] = Dead> of main.C.bar() -> () : $@convention(thin) () -> () // user: %10
      %10 = apply %9() : $@convention(thin) () -> ()
      %11 = integer_literal $Builtin.Int32, 0         // user: %12
      %12 = struct $Int32 (%11 : $Builtin.Int32)      // user: %13
      return %12 : $Int32                             // id: %13
    } // end sil function 'main'
    
    // C.__deallocating_deinit
    sil hidden @main.C.__deallocating_deinit : $@convention(method) (@owned C) -> () {
    // %0                                             // users: %3, %2, %1
    bb0(%0 : $C):
      debug_value %0 : $C, let, name "self", argno 1  // id: %1
      debug_value %0 : $C, let, name "self", argno 1  // id: %2
      dealloc_ref %0 : $C                             // id: %3
      %4 = tuple ()                                   // user: %5
      return %4 : $()                                 // id: %5
    } // end sil function 'main.C.__deallocating_deinit'
    
    // bar()
    sil hidden [noinline] @main.bar() -> () : $@convention(thin) () -> () {
    bb0:
      %0 = tuple ()                                   // user: %1
      return %0 : $()                                 // id: %1
    } // end sil function 'main.bar() -> ()'
    
    // specialized C.bar()
    sil shared [noinline] @function signature specialization <Arg[0] = Dead> of main.C.bar() -> () : $@convention(thin) () -> () {
    bb0:
      %0 = tuple ()                                   // user: %1
      return %0 : $()                                 // id: %1
    } // end sil function 'function signature specialization <Arg[0] = Dead> of main.C.bar() -> ()'
    
    sil_vtable C {
      #C.deinit!deallocator: @main.C.__deallocating_deinit  // C.__deallocating_deinit
    }

    你会注意到只有 bar foo 在SIL的顶部,当SIL降低到LLVM IR时,它们被移除。

    目前还没有一个官方的属性,但有一个 @_optimize(none) 告诉乐观者不要碰东西的属性:

    @_optimize(none) func foo() {}
    

    尽管该属性已加下划线,但使用风险自负。


    出现