背景
plist以其可讀性強(qiáng)的特點(diǎn)撕贞,被大家首選用作配置文件耙旦。但是plist 文件的讀取速度實(shí)在不給力,是否有可選方案找都?答案是有的唇辨!
使用Section 保存數(shù)據(jù)
我們的程序編譯后代碼在.text ,數(shù)據(jù)在.data段能耻,我們可以通過聲明一個字符串在data段的特定的section里(默認(rèn)字符串在 __cstring這個section中)赏枚。
char * str __attribute((used,section("__DATA,Test"))) = "Hello world";
在運(yùn)行的時候通過
///< 頭文件:getsect.h
extern uint8_t *getsectiondata(
const struct mach_header_64 *mhp,
const char *segname,
const char *sectname,
unsigned long *size);
來獲取某個二進(jìn)制文件中亡驰,指定segment指定section的內(nèi)容。這樣子就能從代碼中獲得配置信息饿幅。
Coding
#ifndef __LP64__
#define mach_header mach_header
#else
#define mach_header mach_header_64
#endif
//使用 used字段凡辱,即使沒有任何引用,在Release下也不會被優(yōu)化
#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";
}
)
由于小端序的原因栗恩,所以在鏡像中的地址應(yīng)該是0x0100001a70透乾。 用hopper 打開程序,定位到地址:0x0100001a70磕秤。
通過字符串定位到鏡像文件的指定section乳乌,讀取配置信息。并且實(shí)現(xiàn)了代碼編寫的時候注入市咆,無需文件IO(內(nèi)存讀惹张ぁ),代碼即配置床绪,局部性強(qiáng),可讀性高其弊。
TODO:性能對比
- 對比數(shù)據(jù)從section 和plist 讀取數(shù)據(jù)轉(zhuǎn)化為json的耗時
- 對比數(shù)據(jù)量一樣的前提下癞己,文件大小的對比。
- section 段增大梭伐,是否會對啟動速度有影響痹雅。
更多
關(guān)于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 :