如何在静态库中组合 Swift 和 C 代码

Dag*_*ren 7 c xcode swift

我正在尝试创建一个包含 Swift 和 C 代码的静态库,并且仅公开 Swift 接口。所以C代码端理想情况下不应该暴露给外界。我似乎无法找出在不引起警告或错误的情况下执行此操作的正确方法。

为了简化起见,我尝试使用以下文件设置一个最小的 Swift+C 静态库:

测试.swift

class Test {
    func test() -> CInt {
        return testFromC()
    }
}
Run Code Online (Sandbox Code Playgroud)

测试.c

#include "Test.h"

int testFromC(void) {
    return 0xdeadbeef;
}
Run Code Online (Sandbox Code Playgroud)

测试.h

int testFromC(void);
Run Code Online (Sandbox Code Playgroud)

我想要的是一个公开 Swift 类的静态库Test,但不公开 C 函数testFromC()。不过,如果别无选择,我可以忍受它被曝光。

我尝试过的是:

  1. 添加桥接标头。这几乎可以工作,但是a)很多人说这不被支持或推荐,b)它暴露testFromC()给模块的导入者,c)在某些情况下它会抛出关于隐式导入桥接头的警告(这是事实,但是我想要的是特别不这样做)。

  2. 添加模块映射。我不知道该怎么做。网上似乎没有任何示例适合我,如果有人知道如何正确执行此操作,我很乐意看到这三个文件的示例。

  3. 添加私有模块映射 - 我认为如果我想避免暴露 C 函数,这就是我想要的,但我在这方面的成功更少。

有谁能够整理出构建这个简单示例的确切步骤吗?

Ste*_*cht 6

您确实可以创建私有模块映射,以便不导出 C 符号,请参阅https://clang.llvm.org/docs/Modules.html#private-module-map-files

桥接标头不起作用,因为您想要创建一个框架而不是一个应用程序。

当然有多种方法来设置项目,其中之一是:在 XCode 中使用相应的模块映射设置创建一个项目。

.modulemap文件可能如下所示:

framework module SwiftOnlyAPI {
    umbrella header "SwiftOnlyAPI.h"

    export *
    module * { export * }
}

framework module SwiftOnlyAPI_Private {
    header "Test.h"
}
Run Code Online (Sandbox Code Playgroud)

Test.swift宁愿看起来像这样:

import SwiftOnlyAPI_Private

public class Test {
    public init() {
        //perform some initializations
    }
    
    public func test() -> CInt {
        return testFromC()
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意一些小变化: 的导入SwiftOnlyAPI_Private,这对于未导出但在框架内本地调用的 C 函数是必需的。类和导出的方法必须是公共的,才能从框架外部访问。

指示

  • <File/New Project...> 选择Framework,选择 LanguageSwift命名它SwiftOnlyAPI
  • 将文件(.c、.h、.swift)添加到SwiftOnlyAPI文件夹中
  • Test.h在项目导航器中选择并将检查器中的目标成员资格从 切换ProjectPrivate
  • SwiftOnlyAPI在项目导航器中选择,选择SwiftOnlyAPI目标
  • 现在单击Build Settings并进行Mach-O Type选择:Static Library
  • 仍在Build Settings: 中Module Map File输入:SwiftOnlyAPI/SwiftOnlyAPI.modulemap
  • SwiftOnlyAPI选择项目中的文件夹SwiftOnlyAPINew File...从上下文菜单中选择,然后Empty从该部分中选择模板Other并为其命名SwiftOnlyAPI.modulemap(取消选中目标成员身份)
  • 添加上面显示的 modulemap 内容

测试会产生所需的结果,正如下面针对多个目标的替代解决方案所示。

静态库

框架实际上只是带有附加捆绑文件的动态或静态库(取决于设置)。方便框架用户。

您可以使用以下命令自行测试file

文件命令的输出

选择

如果您希望将 C 部分分开(例如对于其他使用者),您还可以将其拆分为 Xcode 工作区中的两个子项目,一个用于静态 C 库,另一个用于基于静态 Swift 的框架:

  • 创建一个 XCode 工作区 <File/New Workspace...>,例如为其命名SwiftCWorkspace
  • <File/New Project...> 选择Library,在 Framework 下选择None (Plain C/C++ Library),命名MyCLib并选择类型Static
  • 在对话框中选择Add toGroupSwiftCWorkspace
  • 在项目导航器中选择MyCLib节点,New File...从上下文菜单中选择并选择 C ​​文件模板,将其命名为 Test
  • 将内容替换为您的 Test.c 和 Test.h 内容
  • <File/New Project...> 选择Framework,选择 LanguageSwift命名它SwiftOnlyAPI
  • 在对话框中选择Add toGroupSwiftCWorkspace
  • SwiftOnlyAPI选择项目中的文件夹SwiftOnlyAPINew File...从上下文菜单中选择,然后选择Swift模板并将其替换为您的 ccontent
  • SwiftOnlyAPI在项目导航器中选择,选择SwiftOnlyAPI目标
  • 现在单击Build Settings并进行Mach-O Type选择:Static Library
  • 仍在Build Settings: 中Module Map File输入:SwiftOnlyAPI/SwiftOnlyAPI.modulemap
  • 现在单击General并在Frameworks and Library添加部分中libMyCLib.a
  • SwiftOnlyAPI选择项目中的文件夹SwiftOnlyAPINew Group...从上下文菜单中选择并命名MyCLib
  • Test.hMyCLib项目拖放到MyCLib中的文件夹SwiftOnlyAPI,取消选中Copy items if needed
  • 选择新链接的文件后,Test.h将检查器中的目标成员资格从 切换ProjectPrivate
  • SwiftOnlyAPI选择项目中的文件夹SwiftOnlyAPINew File...从上下文菜单中选择,然后Empty从该部分中选择模板Other并为其命名SwiftOnlyAPI.modulemap(取消选中目标成员身份)
  • 添加上面显示的 modulemap 内容

笔记

如果 Xcode 显示错误消息,即使您已正确设置所有内容,有时也需要在项目设置任务后退出 Xcode,删除文件夹~/Library/Developer/Xcode/DerivedData,然后重新启动构建。

测试

import SwiftOnlyAPI

    ...

    let result = SwiftOnlyAPI.Test()
    print(result.test())
Run Code Online (Sandbox Code Playgroud)

您可以使用 SwiftOnlyAPI 框架在单独的应用程序中对此进行测试:将输出正确的结果。

此外,C 函数testFromC是私有的,无法根据需要从外部访问,请参见屏幕截图: 测试