在运行时检查 Swift 中第三方框架的弱链接符号

Dan*_*iel 5 dll frameworks dlsym weak-linking swift

在 macOS 上,我使用必须由用户安装的外部框架(用 C 编写)。在 Swift 中,我需要在运行时检查它是否存在,并且我不能使用 #available() 因为它用于与操作系统相关的功能,并且我正在尝试追踪外部框架。另外,NSClassFromString() 没有用,因为它不是 Objective-C 框架。

我一直在尝试了解如何复制 Objective-C 等效项来检查弱链接符号,例如:

if ( anExternalFunction == NULL ) {
    // fail graciously
} else {
    // do my thing
}
Run Code Online (Sandbox Code Playgroud)

但在 Swift 中,这似乎不起作用:编译器指出,由于 anExternalFunction 不是可选的,所以我总是会得到 != nil,这使得“Swift 有意义”,但对我没有一点帮助。

我找到了两种解决方案,但它们让我的代码变得很糟糕,就像你不会相信的那样:

选项 1,使用名为 isFrameworkAvailable() 的函数创建一个 Objective-C 文件来完成工作,并从 Swift 调用

选项 2,使用以下 Swift 代码实际检查库:

let libHandle = dlopen("/Library/Frameworks/TheLib.framework/TheLib", RTLD_NOW)

if (libHandle != nil) {
    if dlsym(libHandle, "anExternalFunction") != nil {
        return true
    }
}
return false
Run Code Online (Sandbox Code Playgroud)

我一直无法让选项 2 与 RTLD_DEFAULT 很好地配合工作,因为出于某种原因,它是在 dlfcn.h (-2) 中定义的,但似乎没有在 Swift 中导入(就像该标头中的所有负指针一样:RTLD_NEXT、RTLD_DEFAULT 、RTLD_SELF、RTLD_MAIN_ONLY)。我发现了这个最丑陋的黑客让它工作:

if dlsym(unsafeBitCast(-2, to: UnsafeMutableRawPointer.self), "anExternalFunction") != nil {
    // library installed
}
Run Code Online (Sandbox Code Playgroud)

因此,对于选项 2,我需要路径或 hack,我觉得它特别难看(但它有效),而选项 1 不是很“Swifty”,对我来说没有什么意义:在 Swift 中执行此操作的正确方法是什么?

编辑:澄清了问题,更好地解释了选项 2。

Joe*_*ick 1

我发现有两种方法可以做到这一点:

    1.
if let _ = dlopen("/Library/Frameworks/MyLibrary.framework/MyLibrary", RTLD_NOW) {
  print("Can open my library")
}
Run Code Online (Sandbox Code Playgroud)
    2.
#if canImport(MyLibrary)
  print("Can import my library")
#endif
Run Code Online (Sandbox Code Playgroud)

从可读性和类型安全的角度来看,第二个似乎显然更好,但我不确定两者之间的权衡是什么。