[英]Checking at runtime for weakly linked symbols from third-party framework in Swift
On macOS, I use an external framework (written in C) that must be installed by the user. 在macOS上,我使用必须由用户安装的外部框架(用C编写)。 In Swift, I need to check at runtime if it exists, and I can't use #available() since it is for OS-related features, and I am trying to track down an external framework.
在Swift中,我需要在运行时检查它是否存在,并且我不能使用#available(),因为它是与OS相关的功能,我试图追踪外部框架。 Also, NSClassFromString() is not useful since it is not an Objective-C framework.
此外,NSClassFromString()没有用,因为它不是Objective-C框架。
I have been trying to understand how to replicate the Objective-C equivalent for checking for a weakly-linked symbol such as: 我一直试图理解如何复制Objective-C等价物以检查弱链接符号,例如:
if ( anExternalFunction == NULL ) {
// fail graciously
} else {
// do my thing
}
but in Swift, this does not seem to work: the compiler states that since anExternalFunction is not optional, I will always get != nil, which make "Swift sense" but does not help me one bit. 但是在Swift中,这似乎不起作用:编译器声明由于anExternalFunction不是可选的,我总是得到!= nil,这使得“Swift sense”但是对我没有帮助。
I have found two solutions, but they stink up my code like you wouldn't believe: 我找到了两个解决方案,但他们发现我的代码就像你不相信一样:
Option 1, create an Objective-C file with a function called isFrameworkAvailable() doing the work, and calling from Swift 选项1,使用名为isFrameworkAvailable()的函数创建一个Objective-C文件,并执行该工作,并从Swift调用
Option 2, actually checking for the library with the following Swift code: 选项2,实际使用以下Swift代码检查库:
let libHandle = dlopen("/Library/Frameworks/TheLib.framework/TheLib", RTLD_NOW)
if (libHandle != nil) {
if dlsym(libHandle, "anExternalFunction") != nil {
return true
}
}
return false
I have been unable to get Option 2 to work nicely with RTLD_DEFAULT since for some reason, it is defined in dlfcn.h (-2) but does not seem to be imported in Swift (like all negative pointers in that header: RTLD_NEXT, RTLD_DEFAULT, RTLD_SELF, RTLD_MAIN_ONLY). 我一直无法使选项2与RTLD_DEFAULT很好地协同工作,因为出于某种原因,它在dlfcn.h(-2)中定义但似乎没有在Swift中导入(就像该标题中的所有负指针一样:RTLD_NEXT,RTLD_DEFAULT ,RTLD_SELF,RTLD_MAIN_ONLY)。 I have found this ugliest hack to make it work:
我发现这个最丑陋的黑客让它工作:
if dlsym(unsafeBitCast(-2, to: UnsafeMutableRawPointer.self), "anExternalFunction") != nil {
// library installed
}
So for option 2, I require the path or a hack, which I find particularly ugly (but it works) and Option 1 is not very "Swifty" and makes little sense to me: what is the proper way of doing this in Swift? 因此,对于选项2,我需要路径或黑客,我发现它特别难看(但它有效)而且选项1不是非常“Swifty”并且对我来说没什么意义:在Swift中这样做的正确方法是什么?
Edits: clarified question, explained Option 2 better. 编辑:澄清问题,更好地解释选项2。
I've found two ways of doing this: 我发现了两种方法:
if let _ = dlopen("/Library/Frameworks/MyLibrary.framework/MyLibrary", RTLD_NOW) {
print("Can open my library")
}
#if canImport(MyLibrary)
print("Can import my library")
#endif
The second seems clearly better from just a readability and type safety standpoint but I'm not sure what the tradeoffs are between the two. 从可读性和类型安全的角度来看,第二个似乎明显更好,但我不确定两者之间的权衡取舍。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.