原理
如果将BeforeMain阶段用到的方法都找出来出来,让他们在同一或相近的内存页中顺序提前加载,就可以大大减少应用启动时缺页异常出现的概率。
技术要点
(1)枚举出应用启动时会调用到的方法,将其保存到.order文件中
可行的方案是Clang插桩:
首先,我们在项目的Build Settings中搜索并设置 Other C Flags/ Other C++ Flags 为 -fsanitize-coverage=func,trace-pc-guard。如果有swift代码,需要设置 Other Swift Flags 设置为 **** -sanitize-coverage=func -sanitize=undefined, 这样应用启动时所有方法都的边缘都被插入了__sanitizer_cov_trace_pc_guard(...)方法的调用。
然后, 我们只需要实现__sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop)方法, 然后使用dladdr()方法, 就可以将被调用的方法信息枚举出来,然后将其写入.order文件中即可 。
//原子队列
static OSQueueHead symbolList = OS_ATOMIC_QUEUE_INIT;
//定义符号结构体
typedef struct {void *pc;void *next;
}SYNode;- (void)viewDidLoad {[super viewDidLoad];[self genrateOrderFile];
}
void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) {static uint64_t N; // Counter for the guards.if (start == stop || *start) return; // Initialize only once.printf("INIT: %p %p\n", start, stop);for (uint32_t *x = start; x < stop; x++)*x = ++N; // Guards should start from 1.
}
void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {void *PC = __builtin_return_address(0);SYNode *node = malloc(sizeof(SYNode));*node = (SYNode){PC,NULL};//写入队列OSAtomicEnqueue(&symbolList, node, offsetof(SYNode, next));
}
// 解析队列并生成.order文件
- (void) genrateOrderFile{NSMutableArray <NSString *> * symbolNames = [NSMutableArray array];while (YES) {SYNode * node = OSAtomicDequeue(&symbolList, offsetof(SYNode, next));if (node == NULL) {break;}Dl_info info;dladdr(node->pc, &info);NSString * name = @(info.dli_sname);BOOL isObjc = [name hasPrefix:@"+["] || [name hasPrefix:@"-["];NSString * symbolName = isObjc ? name: [@"_" stringByAppendingString:name];[symbolNames addObject:symbolName];}//取反NSEnumerator * enumerator = [symbolNames reverseObjectEnumerator];//去重NSMutableArray<NSString *> *funcs = [NSMutableArray arrayWithCapacity:symbolNames.count];NSString * name;while (name = [enumerator nextObject]) {if (![funcs containsObject:name]) {[funcs addObject:name];}}[funcs removeObject:[NSString stringWithFormat:@"%s",__FUNCTION__]];//写入.order文件NSString * funcStr = [funcs componentsJoinedByString:@"\n"];NSString * filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"demo.order"];NSData * fileContents = [funcStr dataUsingEncoding:NSUTF8StringEncoding];[[NSFileManager defaultManager] createFileAtPath:filePath contents:fileContents attributes:nil];NSLog(@"%@",funcStr);
}
@end
(2)设置应用在链接时, 根据.order文件顺序加载其中的符号
只需在项目的Build Settings中搜索“Order File”,配置.order文件即可