在 macOS 上,我使用了一个外部的 C 语言编写的框架,必须由用户安装。在 Swift 中,我需要在运行时检查它是否存在,但我不能使用 #available(),因为它只是用于检测操作系统相关的特性,而我需要追踪一个外部框架。另外,NSClassFromString() 对于非 Objective-C 框架也没有用。
我一直在尝试理解如何复制 Objective-C 的等效方法来检查弱链接符号,例如:
if ( anExternalFunction == NULL ) {
// fail graciously
} else {
// do my thing
}
但在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
因为某种原因,我无法将选项2与RTLD_DEFAULT很好地配合使用,因为它在dlfcn.h中被定义(-2),但似乎没有被Swift导入(就像该头文件中的所有负指针一样:RTLD_NEXT、RTLD_DEFAULT、RTLD_SELF、RTLD_MAIN_ONLY)。我发现了这个最丑陋的hack来使它正常工作:
if dlsym(unsafeBitCast(-2, to: UnsafeMutableRawPointer.self), "anExternalFunction") != nil {
// library installed
}
对于Option 2,我需要路径或一种hack方法,虽然这种方法特别丑陋(但有效),而Option 1不太“Swifty”,并且对我来说没有什么意义:在Swift中正确的做法是什么?
#define RTLD_DEFAULT ((void *) -2
这样的 C 宏并没有被导入到 Swift 中,但是if dlsym(UnsafeMutableRawPointer(bitPattern: -2), "anExternalFunction") ...
会更加简洁。- 参考 https://forums.developer.apple.com/thread/18816。 - Martin R