iOS中使用Mach-O section进行存储

理论

编译器编译后生成的文件叫目标文件,从文件结构来说,它已经是编译后可执行的文件,只是还没有经过链接的过程。我们程序编译后的代码在.text,数据.data中。编译器函数在编译时把数据写入可执行文件的.data段中,运行时再从.data段中取出数据进行相应的操作。借助.data段,能够覆盖所有的启动阶段,例如main()之前的阶段。

Clang提供了很多的编译器函数,它们可以完成不同的功能。其中一种是section()函数,section()函数提供了二进制段的读写能力,它可以将一些编译期就可以确定的常量写入数据段。在具体的实现中,主要分为编译期和运行时两个部分。在编译期,编译器会将标记了attribute((section()))的数据写到指定的数据段,例如写一个{key(key代表不同的启动阶段), *pointer}对到数据段。到运行时,在合适的时间节点,再根据key读取出函数指针,完成函数的调用。

使用_ _attribute方法注入

/**
被used修饰以后,意味着即使函数没有被引用,在Release下也不会被优化。如果不加这个修饰,那么Release环境链接器下会去掉没有被引用的段
*/
#define HMAnnotationDATA __attribute((used, section("__DATA,HMSectionStore")))

/**
 *  Use this to annotation a `module`
 *  like this: HMSectionStoreModule()
 */
#define HMSectionStoreModule(modName) \
char * kHMSectionStoreModule_##modName HMAnnotationDATA = "M:"#modName"";

/**
调用上面定义好的宏
*/
HMSectionStoreModule(HMSectionStoreViewController)

获取存储数据

NSArray* HMSectionStoreReadConfigFromSection(const char *sectionName){
    #ifndef __LP64__
        const struct mach_header *mhp = NULL;
    #else
        const struct mach_header_64 *mhp = NULL;
    #endif
        
        NSMutableArray *configs = [NSMutableArray array];
        Dl_info info;
        if (mhp == NULL) {
            dladdr(HMSectionStoreReadConfigFromSection, &info);
    #ifndef __LP64__
            mhp = (struct mach_header*)info.dli_fbase;
    #else
            mhp = (struct mach_header_64*)info.dli_fbase;
    #endif
        }
        
    #ifndef __LP64__
        unsigned long size = 0;
     // 找到之前存储的数据段的一片内存
        uint32_t *memory = (uint32_t*)getsectiondata(mhp, SEG_DATA, sectionName, & size);
    #else /* defined(__LP64__) */
        unsigned long size = 0;
        uint64_t *memory = (uint64_t*)getsectiondata(mhp, SEG_DATA, sectionName, & size);
    #endif /* defined(__LP64__) */
        
        for(int idx = 0; idx < size/sizeof(void*); ++idx){
            char *string = (char*)memory[idx];
            // 把特殊段里面的数据都转换成字符串存入数组中
            NSString *str = [NSString stringWithUTF8String:string];
            if(!str)continue;
            if(str) [configs addObject:str];
        }
        return configs;
}

完整的代码

#import "HMSectionStoreViewController.h"
#include 
#include 
#include 
#include 

#define HMAnnotationDATA __attribute((used, section("__DATA,HMSectionStore")))

/**
 *  Use this to annotation a `module`
 *  like this: @HMSectionStoreModule()
 */
#define HMSectionStoreModule(modName) \
char * kHMSectionStoreModule_##modName HMAnnotationDATA = "M:"#modName"";



NSArray* HMSectionStoreReadConfigFromSection(const char *sectionName){
    #ifndef __LP64__
        const struct mach_header *mhp = NULL;
    #else
        const struct mach_header_64 *mhp = NULL;
    #endif
        
        NSMutableArray *configs = [NSMutableArray array];
        Dl_info info;
        if (mhp == NULL) {
            dladdr(HMSectionStoreReadConfigFromSection, &info);
    #ifndef __LP64__
            mhp = (struct mach_header*)info.dli_fbase;
    #else
            mhp = (struct mach_header_64*)info.dli_fbase;
    #endif
        }
        
    #ifndef __LP64__
        unsigned long size = 0;
     // 找到之前存储的数据段的一片内存
        uint32_t *memory = (uint32_t*)getsectiondata(mhp, SEG_DATA, sectionName, & size);
    #else /* defined(__LP64__) */
        unsigned long size = 0;
        uint64_t *memory = (uint64_t*)getsectiondata(mhp, SEG_DATA, sectionName, & size);
    #endif /* defined(__LP64__) */
        
        for(int idx = 0; idx < size/sizeof(void*); ++idx){
            char *string = (char*)memory[idx];
            // 把特殊段里面的数据都转换成字符串存入数组中
            NSString *str = [NSString stringWithUTF8String:string];
            if(!str)continue;
            if(str) [configs addObject:str];
        }
        return configs;
}

/**
编译的时候,存储数据到.data中
*/
HMSectionStoreModule(HMSectionStoreViewController)
@interface HMSectionStoreViewController ()

@end

@implementation HMSectionStoreViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    //获取注入的数据
    NSArray *dataListInSection = HMSectionStoreReadConfigFromSection("HMSectionStore");
    for (NSString *dataString in dataListInSection) {
        NSLog(@"获取section中存储的数据--> %@",dataString);
    }
}
@end

运行结果

Snip20200414_1.png

运用

路由协议中,模块类容加载到内存中。可参考
AppLord

BeeHive

相关参考内容

美团外卖iOS App冷启动治理

你可能感兴趣的:(iOS中使用Mach-O section进行存储)