Use Mach-O section as plist

背景

plist以其可读性强的特点,被大家首选用作配置文件。但是plist 文件的读取速度实在不给力,是否有可选方案?答案是有的!

使用Section 保存数据

我们的程序编译后代码在.text ,数据在.data段,我们可以通过声明一个字符串在data段的特定的section里(默认字符串在 __cstring这个section中)。

char * str __attribute((used,section("__DATA,Test"))) = "Hello world";

在运行的时候通过

///< 头文件:getsect.h
extern uint8_t *getsectiondata(
    const struct mach_header_64 *mhp,
    const char *segname,
    const char *sectname,
    unsigned long *size);

来获取某个二进制文件中,指定segment指定section的内容。这样子就能从代码中获得配置信息。

Coding

#ifndef __LP64__
#define mach_header mach_header
#else
#define mach_header mach_header_64
#endif

//使用 used字段,即使没有任何引用,在Release下也不会被优化
#define WriteSection(sectName) __attribute((used, section("__DATA,"#sectName" ")))
#define SectionDataWithKeyValue(key,value) char * k##key WriteSection(CustomSection) = "{ \""#key"\" : \""#value"\"}";

// 注入 :
SectionDataWithKeyValue(url, www.baidu.com)


#import "ViewController.h"
#include 
#include 
#include 
#include 

static NSString *configuration = @"";
const struct mach_header *machHeader = NULL;

@interface ViewController ()
@end

@implementation ViewController

- (void)viewDidLoad {
    
    [super viewDidLoad];
    NSLog(@"%@",[self readConfigFromSectionName:@"CustomSection"]);
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (NSArray  *)readConfigFromSectionName:(NSString *)sectionName
{
    NSMutableArray *configs = [NSMutableArray array];
    if (sectionName.length)
    {
        if (machHeader == NULL)
        {
            Dl_info info;
            dladdr((__bridge const void *)(configuration), &info);
            machHeader = (struct mach_header*)info.dli_fbase;
        }
        unsigned long size = 0;
        uintptr_t *memory = (uintptr_t*)getsectiondata(machHeader, SEG_DATA, [sectionName UTF8String], & size);
        
        NSUInteger counter = size/sizeof(void*);
        NSError *converError = nil;
        for(int idx = 0; idx < counter; ++idx){
            char *string = (char*)memory[idx];
            
            NSString *str = [NSString stringWithUTF8String:string];
            NSData *data = [str dataUsingEncoding:NSUTF8StringEncoding];
            id json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&converError];
            if (json && [json isKindOfClass:[NSDictionary class]])
            {
                [configs addObject:json];
            }
        }
    }
    return configs;
}
@end

最终输出:

2017-06-05 22:41:54.431 WriteSectionData[27360:1459771] (
        {
        url = "www.baidu.com";
    }
)
Use Mach-O section as plist_第1张图片
WriteSectionData.png

由于小端序的原因,所以在镜像中的地址应该是0x0100001a70。 用hopper 打开程序,定位到地址:0x0100001a70。

Use Mach-O section as plist_第2张图片
WriteSectionData_hop.png

通过字符串定位到镜像文件的指定section,读取配置信息。并且实现了代码编写的时候注入,无需文件IO(内存读取),代码即配置,局部性强,可读性高。

TODO:性能对比

  • 对比数据从section 和plist 读取数据转化为json的耗时
  • 对比数据量一样的前提下,文件大小的对比。
  • section 段增大,是否会对启动速度有影响。

更多

关于DATA 段大小的限制

App Size for iOS (& tvOS) only

Your app’s total uncompressed size must be less than 4GB. Each Mach-O executable file (for example, app_name.app/app_name) must not exceed these limits:

For apps whose MinimumOSVersion is less than 7.0: maximum of 80 MB for the total of all __TEXT sections in the binary.
For apps whose MinimumOSVersion is 7.x through 8.x: maximum of 60 MB per slice for the __TEXT section of each architecture slice in the binary.
For apps whose MinimumOSVersion is 9.0 or greater: maximum of 500 MB for the total of all __TEXT sections in the binary.
However, consider download times when determining your app’s size. Minimize the file’s size as much as possible, keeping in mind that there is a 100 MB limit for over-the-air downloads.

以上引用自苹果官方文档,官方只给出了TEXT段的大小限制,并没有给出DATA 段的大小限制。
Ref :

Specifying Attributes of Variables

你可能感兴趣的:(Use Mach-O section as plist)