ebb*_*y94 5 objective-c ios swift module-map swift-framework
我正在开发一个 Swift 动态框架,其中包含一些 Objective-C 代码。我需要使用一些遗留的 Objective-C 代码而不将代码暴露给它。我开始知道我可以使用模块映射来避免将标头添加为框架中的公共标头。我一直在使用有关如何创建模块映射的链接。我创建的模块映射看起来像这样。
framework module Xyz {
umbrella header "Xyz.h"
export *
module * { export * }
}
framework module Module1{
header "Module1.h"
}
framework module Module2{
header "Module2.h"
}
.... Rest of the modules
Run Code Online (Sandbox Code Playgroud)
之后,我尝试在 Swift 文件中导入模块,但没有自动完成。无论如何,我写了模块名称并尝试构建它。我在 Swift 文件中收到错误“无法构建 Objective-C 模块”。在模块映射文件中,我看到错误为“无法找到标头”。这是错误
所以我尝试添加构建设置的$(SRCROOT)/ProjectName/路径。Include Path之后就找不到伞头了。
我搜索了大部分 StackOverflow 和 Google,但找不到任何对我有帮助的东西。这是我第一次使用模块映射,我被困在这里,不知道该怎么做才能解决问题。如果有人能指出我错过了什么,或者您可以建议一些其他可以帮助我的来源,那就太好了。谢谢!
如果您不希望 Objective-C 符号从库中公开,您所需要做的就是将标志传递-fvisibility=hidden给编译器,然后所有非静态符号仅在库本身内可用,而不会添加到最终符号中当库被链接时,如果不将它们添加到符号表中,则无法从外部直接访问它们。
这个设置甚至存在于 Xcode 中,它被调用Symbols Hidden by Default并在 xcconfig 文件中被命名GCC_SYMBOLS_PRIVATE_EXTERN(GCC,因为它是一个旧设置,当 Xcode 仍然使用 GCC 而不是 Clang 时就已经存在)。它是一个布尔值,当设置为 YES 时,Xcode 会传递-fvisibility=hidden给编译器。
如果您想公开单个符号,例如特定的类,您可以使用属性注释来实现:
__attribute__((visibility("default")))
@interface MyClass : NSObject
Run Code Online (Sandbox Code Playgroud)
现在符号 forMyClass再次添加到符号表中。
请注意,这对 Swift 代码影响为零。Swift 符号是否添加到符号表取决于它们的访问修饰符。如果它们是public或open,它们将被添加到符号表中。Swift 符号的默认值是,这与在 Obj-C 中module标记符号相同(除非另有说明,否则默认情况下对所有符号都如此)。最后还有Swift 中的and ,它与 C 中的类似,它甚至根本不生成符号,因此甚至不可能从当前翻译单元之外引用它。__attribute__((visibility("hidden")))-fvisibility=hiddenprivatefileprivatestatic
请注意,即使没有符号,也可以通过 Objective-C 运行时访问 Objective-C 类,因为每个 Obj-C 类都必须在运行时注册才能用作普通 Obj-C 类(模块映射也无法阻止这种情况),所以你仍然可以做类似的事情
[[NSClassFromString(@"MyClass") alloc] init];
Run Code Online (Sandbox Code Playgroud)
可见性不会阻止它的工作,因为您不是通过其符号访问该类,而是通过它的名称作为字符串,但是如果您确实不需要从库外部访问类,则可以绕过 Objective -C 运行时。
__attribute__((objc_direct_members))
__attribute__((objc_subclassing_restricted))
@interface MyClass : NSObject
Run Code Online (Sandbox Code Playgroud)
现在你的类根本不使用 Objective-C 运行时,所有对方法的调用实际上都是直接调用,这意味着它们不使用 Obj-C 运行时调度,它们直接像 C 函数调用一样调用实现(这也是使方法调用速度更快)。因此,即使有人知道您的类的名称、方法的名称及其签名,他也无法从外部调用使用任何这些内容。
直接调度是苹果公司不久前推出的。例如,如果您不在公共接口上使用此属性
__attribute__((visibility("default")))
@interface ClassWithPublicInterface : NSObject
Run Code Online (Sandbox Code Playgroud)
但你将其添加到实现中
__attribute__((objc_direct_members))
@implementation ClassWithPublicInterface
Run Code Online (Sandbox Code Playgroud)
发生以下情况:
该接口定义的所有方法都是公开可用的,并使用 Obj-C 运行时进行调度。但是,所有仅在实现中声明的方法都是私有的,可以直接分派,并且不能从类外部访问。
例如
__attribute__((objc_direct_members))
@implementation ClassWithPublicInterface
- (int)add:(int)a to:(int)b { return a + b; }
@end
Run Code Online (Sandbox Code Playgroud)
假设您没有add:to:在公共标头中声明该方法,则该方法实际上就像您编写的一样
__attribute__((objc_direct_members))
@implementation ClassWithPublicInterface
static int add(int a, int b) { return a + b; }
@end
Run Code Online (Sandbox Code Playgroud)
尽管它是一种方法并允许您访问对象的实例变量。无法从外部访问此实现,并且调用它与调用静态 C 方法一样快。
但请注意,直接调度方法不能被子类覆盖。如果您尝试这样做,您不会收到错误消息,但您的重写方法根本不会被调用。这可以防止意外覆盖 Obj-C 中父类的方法(因为您可以覆盖标头中未公开的方法,因此您甚至不知道您正在覆盖方法),但它也可能导致微妙且难以跟踪错误。
这就是为什么我添加__attribute__((objc_subclassing_restricted))到上面的示例中,这final在 Obj-C 中创建了一个类。任何对此类进行子类化的尝试都将直接导致编译器错误。还知道此类永远不会子类化,因此可以执行一些额外的编译器优化。
最后但并非最不重要的一点是,您可以将单个属性和方法标记为直接:
@interface MixedClass : NSObject
@property int indirect;
@property(direct) int direct;
- (int)processDataIndirect:(NSData *)data;
- (int)processDataDirect:(NSData *)data __attribute__((objc_direct));
@end
Run Code Online (Sandbox Code Playgroud)
你为什么想这么做?正如我上面所说,直接调用与 C 函数调用一样快,间接调用则慢几倍,因为它必须执行方法查找表(这是一个哈希表,键是方法名称,值是方法)的查找指针)首先,这就是 Obj-C 允许在运行时动态调度和覆盖方法的方式。缺点是,直接调用不能被覆盖,并且在当前模块外部不可见(例如,在当前框架/库/二进制文件之外)。
顺便说一句:您的调用代码不需要知道或关心方法/属性是否直接,您的代码在两种情况下看起来都是相同的,只是编译器将为这两种情况生成不同的代码。