如何按名称从全局作用域获取Objective-C常量、函数、枚举及协议
你提到的四类符号均可以通过运行时、动态链接接口或编译期辅助逻辑实现按名称查找,以下是分场景的具体实现方案:
1. 常量/独立枚举值
- 枚举字面量(例如
NSASCIIStringEncoding)属于编译期确定的整型常量,不会生成对应的运行时符号,无法通过动态链接接口直接查找,你可以提前维护目标枚举的「名称-值」映射表,也可以基于Clang的枚举反射扩展在编译期自动生成映射表供查询。 - 被
extern修饰的全局常量(例如系统通知名、全局字符串常量)可以直接通过动态链接接口dlsym获取,示例代码如下:
// 查找全局通知名常量 NSString *notificationName = (__bridge NSString *)dlsym(RTLD_DEFAULT, "UIApplicationDidFinishLaunchingNotification");
2. 枚举类型
枚举类型属于编译期的类型定义,默认不会导出到运行时符号表,没有通用的运行时查找接口:
- 针对OC的
NS_ENUM/NS_OPTIONS定义的公开枚举,你可以在接口层维护「枚举类型名-底层类型、枚举值集合」的元数据映射,供JS侧查询枚举信息。 - 针对C枚举类型,可以借助C20编译期反射特性,或者Clang的编译扩展自动生成枚举元数据映射,不需要手动维护。
3. 全局C函数
NSSelectorFromString仅用于获取OC方法选择子,无法用于查找全局C函数,全局C函数可以直接通过dlsym获取函数指针,转换为对应类型后即可调用,示例代码如下:
// 先定义与目标函数匹配的函数指针类型 typedef NSString *(*NSStringFromProtocolFunc)(Protocol *); // 从全局作用域查找函数符号 NSStringFromProtocolFunc func = (NSStringFromProtocolFunc)dlsym(RTLD_DEFAULT, "NSStringFromProtocol"); if (func) { // 调用函数 NSString *protoName = func(@protocol(NSObject)); }
注意如果是你自己导出的C++函数,需要加extern "C"修饰避免名字修饰,否则无法直接按原函数名查找符号。
4. 协议
协议可以直接使用系统提供的NSProtocolFromString接口获取,示例代码如下:
Protocol *delegateProto = NSProtocolFromString(@"NSURLConnectionDataDelegate");
注意只有被显式引用过(例如被OC类实现、被@protocol()表达式引用)的协议才会被编译到二进制中,仅在头文件定义未被引用的协议会被编译器优化掉,导致查找失败。
限定指定库作用域查找
如果你需要限定只从某个指定的动态库中查找符号,可以先通过dlopen获取目标库的句柄,再用该句柄调用dlsym即可:
// 打开Foundation.framework的动态库,获取句柄 void *foundationHandle = dlopen("/System/Library/Frameworks/Foundation.framework/Foundation", RTLD_LAZY); if (foundationHandle) { // 仅从Foundation库中查找符号 NSStringFromProtocolFunc func = (NSStringFromProtocolFunc)dlsym(foundationHandle, "NSStringFromProtocol"); // 用完后关闭句柄 dlclose(foundationHandle); }
内容的提问来源于stack exchange,提问作者Jamie Birch
相关产品推荐
相关产品推荐

