问题:如果某些业务场景下,需要很早的注册一些key-value? 怎么办?
很多同学第一反应是+Load方法中进行注册;
当然可以,如果需要在程序编译/链接期间就执行呢?
答案:
可以,我们可以通过attribute((section("name")))编译属性将数据写到可执行文件中,然后在使用的时候,从可执行文件中读取出来.
解释:
attribute是编译属性,用于改变所声明或定义的函数或数据的特性,比如存储特性。
首先定义一个结构体:
struct ProtocolInfo{
char *key;
char *value;
};
然后,使用宏定义一段使用attribute((section("name"))) 的代码段:
#define ProtocolRegister(_key_,_value_)\
__attribute__((used)) static struct ProtocolInfo ProtocolInfo##_key_ \
__attribute__ ((used, section ("__DATA,ProtocolInfoData"))) =\
{\
.key = #_key_,\
.value = #_value_,\
};
used是告诉编译器不用优化掉此函数,即使没有地方使用。ProtocolInfoData 名字可以自定义,除了结构体,char,int 类型也可以使用。这样我们就可以将协议数据写到可执行文件的__DATA 字段中了。
使用方式:
ProtocolRegister(home,jump1)
ProtocolRegister(my,jump2)
在可执行文件(Mach-O)中的格式如下:
Mach-O文件
读取
#include <dlfcn.h>
#include <mach-o/getsect.h>
...
- (void)readDataFromMachO {
//1.根据符号找到所在的mach-o文件信息
Dl_info info;
dladdr((__bridge void *)[self class], &info);
//2.读取__DATA中自定义的ProtocolInfoDataz数据
#ifndef __LP64__
const struct mach_header *mhp = (struct mach_header*)info.dli_fbase;
unsigned long schemeSize = 0;
uint32_t *schemeMemory = (uint32_t*)getsectiondata(mhp, "__DATA", "ProtocolInfoData", &schemeSize);
#else /* defined(__LP64__) */
const struct mach_header_64 *mhp = (struct mach_header_64*)info.dli_fbase;
unsigned long schemeSize = 0;
uint64_t *schemeMemory = (uint64_t*)getsectiondata(mhp, "__DATA", "ProtocolInfoData", &schemeSize);
#endif /* defined(__LP64__) */
//3.遍历ProtocolInfoData中的协议数据
unsigned long schemeCounter = schemeSize/sizeof(struct ProtocolInfo);
struct ProtocolInfo *items = (struct ProtocolInfo*)schemeMemory;
for(int idx = 0; idx < schemeCounter; ++idx){
NSString * key = [NSString stringWithUTF8String:items[idx].key];
NSString * value = [NSString stringWithUTF8String:items[idx].value];;
NSLog(@"-------------key:%@ , value:%@",key,value);
}
}
除了__attribute__((section("name")))
,常用的如使用__attribute__((constructor))
修饰的函数可以在main函数之前调用 ,可以替换load方法使用。
网友评论