有没有办法在 Swift 中声明一个内联函数?

Is there a way to declare an inline function in Swift?

我对 Swift 语言还很陌生。

我想像在 C++ 中一样声明一个 inline 函数 所以我的 func 声明如下所示:

func MyFunction(param: Int) -> Int {
    ...
}

我想做这样的事情:

inline func MyFunction(param: Int) -> Int {
    ...
}

我试着在网上搜索,但我没有找到任何相关的东西,也许没有 inline 关键字,但也许有另一种方法可以在 Swift.[=15 中内联函数=]

Swift 1.2 将包含 @inline 属性,参数为 never__always。有关详细信息,请参阅 here

如前所述,您很少需要将函数显式声明为 @inline(__always),因为 Swift 对于何时内联函数相当聪明。但是,在某些代码中可能需要不内联函数。

编辑:Swift 5 添加了 @inlinable 属性。请务必阅读相关内容,因为可能存在一些可能导致无法使用的陷阱。它也仅适用于 functions/methods 声明的 public,因此它适用于那些想要为那些 link 向您的库公开内联内容的人。

全部归功于, just summarizing the information from the link

要使函数内联只需在函数前添加 @inline(__always):

@inline(__always) func myFunction() {

}

但是,值得考虑和了解不同的可能性。有三种可能的内联方式:

  • 有时 - 将确保有时内联函数。这是默认行为,您无需执行任何操作! Swift 编译器可能会自动内联函数作为优化。
  • always - 将确保始终内联函数。通过在函数前添加 @inline(__always) 来实现此行为。使用 "if your function is rather small and you would prefer your app ran faster."
  • never - 将确保永远不会内联函数。这可以通过在函数前添加 @inline(never) 来实现。使用 "if your function is quite long and you want to avoid increasing your code segment size."

我遇到了一个问题,我需要使用 Swift 4.2 中引入的 @inlinable@usableFromInline 属性,所以我想与您分享我的经验。

但让我直接进入问题,我们的代码库有一个链接其他模块的 Analytics Facade 模块。

App Target -> Analytics Facade 模块 -> Reporting 模块 X.

Analytics Facade 模块有一个名为 report(_ rawReport: EventSerializable) 的函数可以触发报告调用,该函数使用来自报告模块 X 的实例来发送针对特定报告模块 X 的报告调用。

问题是,一旦用户启动应用程序,多次调用 report(_ rawReport: EventSerializable) 函数来发送报告调用会产生不可避免的开销,导致我们发生很多崩溃。

此外,如果您在调试模式下将 Optimisation level 设置为 None,则重现这些崩溃并非易事。在我的例子中,当我将 Optimisation level 设置为 Fastest, Smallest 或更高时,我只能重现它。

解决方案是使用 @inlinable@usableFromInline

使用 @inlinable@usableFromInline 将函数体导出为模块接口的一部分,使其在从其他模块引用时可供优化器使用。

@usableFromInline 属性将内部声明标记为模块二进制接口的一部分,允许它在 @inlinable 代码中使用,而不会将其作为模块源接口的一部分公开。

跨模块边界,运行时泛型引入了不可避免的开销,因为必须在函数之间传递具体化的类型元数据,并且必须使用各种间接访问模式来操作泛型类型的值。对于大多数应用程序,与代码本身执行的实际工作相比,这种开销可以忽略不计。

基于此框架构建的客户端二进制文件可以调用这些泛型函数,并且由于消除了抽象开销,因此在启用优化的情况下构建时可能会提高性能。

示例代码:

@inlinable public func allEqual<T>(_ seq: T) -> Bool
    where T : Sequence, T.Element : Equatable {
        var iter = seq.makeIterator()
        guard let first = iter.next() else { return true }

        func rec(_ iter: inout T.Iterator) -> Bool {
            guard let next = iter.next() else { return true }
            return next == first && rec(&iter)
        }

        return rec(&iter)
}

More Info - Cross-module inlining and specialization