背景
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 <mach-o/getsect.h>
#include <mach-o/loader.h>
#include <mach-o/dyld.h>
#include <dlfcn.h>
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 <NSDictionary *> *)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";
}
)
WriteSectionData.png
由于小端序的原因,所以在镜像中的地址应该是0x0100001a70。 用hopper 打开程序,定位到地址:0x0100001a70。
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 :
网友评论