iOS优化篇之App启动时间优化
Posted iOSer
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS优化篇之App启动时间优化相关的知识,希望对你有一定的参考价值。
app启动过程
premain
加载所有依赖的Mach-O文件(递归调用Mach-O加载的方法)
加载动态链接库加载器dyld(dynamic loader)
定位内部、外部指针引用,例如字符串、函数等
加载类扩展(Category)中的方法
C++静态对象加载、调用ObjC的 +load 函数
执行声明为attribute((constructor))的C函数
main
调用main()
调用UIApplicationMain()
调用applicationWillFinishLaunching
一、耗时方法优化
1.统计启动时的耗时方法
double launchTime = CFAbsoluteTimeGetCurrent();
[SDWebImageManager sharedManager];
NSLog(@"launchTime = %f秒", CFAbsoluteTimeGetCurrent() - launchTime);
二、+load方法优化以及删减不用的类
2.1 +load方法统计
2.2 使用__attribute优化+load方法
#ifndef ZYStoreListTemplateSectionName
#define ZYStoreListTemplateSectionName "ZYTempSection"
#endif
#define ZYStoreListTemplateDATA(sectname) __attribute((used, section("__DATA,"#sectname" ")))
#define ZYStoreListTemplateRegister(templatename,templateclass)
class NSObject; char * k##templatename##_register ZYStoreListTemplateDATA(ZYTempSection) = "{ ""#templatename"" : ""#templateclass""}";
/**
通过ZYStoreListTemplateRegister(key,classname)注册处理模板的类名(类必须是ZYStoreListBaseTemplate子类)
【注意事项】
该方式通过__attribute属性在编译期间绑定注册信息,运行时读取速度快,注册信息在首次触发调用时读取,不影响pre-main时间
该方式注册时‘key’字段中不支持除下划线'_'以外的符号
【使用示例】
注册处理模板的类名:@ZYStoreListTemplateRegister(baseTemp,ZYStoreListBaseTemplate)
**/
#pragma mark - 第一次使用时读取ZYStoreListTemplateSectionName的__DATA所有数据
+ (void)readTemplateDataFromMachO {
//1.根据符号找到所在的mach-o文件信息
Dl_info info;
dladdr((__bridge void *)[self class], &info);
//2.读取__DATA中自定义的ZYStoreListTemplateSectionName数据
#ifndef __LP64__
const struct mach_header *mhp = (struct mach_header*)info.dli_fbase;
unsigned long templateSize = 0;
uint32_t *templateMemory = (uint32_t*)getsectiondata(mhp, "__DATA", ZYStoreListTemplateSectionName, &templateSize);
#else /* defined(__LP64__) */
const struct mach_header_64 *mhp = (struct mach_header_64*)info.dli_fbase;
unsigned long templateSize = 0;
uint64_t *templateMemory = (uint64_t*)getsectiondata(mhp, "__DATA", ZYStoreListTemplateSectionName, &templateSize);
#endif /* defined(__LP64__) */
//3.遍历ZYStoreListTemplateSectionName中的协议数据
unsigned long counter = templateSize/sizeof(void*);
for(int idx = 0; idx < counter; ++idx){
char *string = (char*)templateMemory[idx];
NSString *str = [NSString stringWithUTF8String:string];
if(!str)continue;
//NSLog(@"config = %@", str);
NSData *jsonData = [str dataUsingEncoding:NSUTF8StringEncoding];
NSError *error = nil;
id json = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error];
if (!error) {
if ([json isKindOfClass:[NSDictionary class]] && [json allKeys].count) {
NSString *templatesName = [json allKeys][0];
NSString *templatesClass = [json allValues][0];
if (templatesName && templatesClass) {
[self registerTemplateName:templatesName templateClass:NSClassFromString(templatesClass)];
}
}
}
}
}
三、二进制重排
3.1 获取Order File
Target -> Build Setting -> Custom Complier Flags -> Other C Flags
添加
-fsanitize-coverage=func,trace-pc-guard
参数
#import "dlfcn.h"
#import <libkern/OSAtomic.h>
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 ", start, stop);
for (uint32_t *x = start; x < stop; x++)
*x = ++N; // Guards should start from 1.
}
//原子队列
static OSQueueHead symboList = OS_ATOMIC_QUEUE_INIT;
static BOOL isEnd = NO;
//定义符号结构体
typedef struct{
void * pc;
void * next;
}SymbolNode;
void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {
//if (!*guard) return; // Duplicate the guard check.
if (isEnd) {
return;
}
void *PC = __builtin_return_address(0);
SymbolNode * node = malloc(sizeof(SymbolNode));
*node = (SymbolNode){PC,NULL};
//入队
// offsetof 用在这里是为了入队添加下一个节点找到 前一个节点next指针的位置
OSAtomicEnqueue(&symboList, node, offsetof(SymbolNode, next));
}
- (void)getAllSymbols {
isEnd = YES;
NSMutableArray<NSString *> * symbolNames = [NSMutableArray array];
while (true) {
//offsetof 就是针对某个结构体找到某个属性相对这个结构体的偏移量
SymbolNode * node = OSAtomicDequeue(&symboList, offsetof(SymbolNode, 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];
//去重
if (![symbolNames containsObject:symbolName]) {
[symbolNames addObject:symbolName];
}
}
//取反
NSArray * symbolAry = [[symbolNames reverseObjectEnumerator] allObjects];
NSLog(@"%@",symbolAry);
//将结果写入到文件
NSString * funcString = [symbolAry componentsJoinedByString:@" "];
NSString * filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"linkSymbols.order"];
NSData * fileContents = [funcString dataUsingEncoding:NSUTF8StringEncoding];
BOOL result = [[NSFileManager defaultManager] createFileAtPath:filePath contents:fileContents attributes:nil];
if (result) {
NSLog(@"linkSymbol result %@",filePath);
}else{
NSLog(@"linkSymbol result文件写入出错");
}
}
post_install do |installer|
pods_project = installer.pods_project
build_settings = Hash[
'OTHER_CFLAGS' => '-fsanitize-coverage=func,trace-pc-guard'
# ,'OTHER_SWIFT_FLAGS' => '-sanitize=undefined -sanitize-coverage=func'
]
pods_project.targets.each do |target|
# if !target.name.include?('Pods-')
if !target.name.include?('Pods-') and target.name != 'LookinServer' and target.name != 'DoraemonKit' and target.name != 'DoraemonKit-DoraemonKit'
# 修改build_settings
target.build_configurations.each do |config|
build_settings.each do |pair|
key = pair[0]
value = pair[1]
if config.build_settings[key].nil?
config.build_settings[key] = ['']
end
if !config.build_settings[key].include?(value)
config.build_settings[key] << value
end
end
end
puts '[Other C Flags]: ' + target.name + ' success.'
end
end
end
3.1 设置Order File
Target -> Build Setting -> Linking -> Order File
添加上述步骤导出的函数符号列表linkSymbols.order。
$(SRCROOT)/linkSymbols.order
这里可以根据根目录路径然后寻找,不必把orderfile添加到工程bundle里。如果添加到工程里则会被打包到ipa里。我们可以只是放在工程文件夹下,只在编译的时候根据路径引用就可以了。
($Project)-LinkMap-normal-arm64.txt
。修改完毕后 clean 一下 , 运行工程 , Products - show in finder, 找到 macho 的上上层目录。找到结尾为arm64.txt的文件并打开。
Intermediates -> project_ios.build -> Debug-iphoneos -> project_ios.build -> project_ios-LinkMap-normal-arm64.txt
($Project)-LinkMap-normal-arm64.txt
文件里在
#Symbols
之后为函数符号链接的顺序,可以验证一下重排是否成功。
以上是关于iOS优化篇之App启动时间优化的主要内容,如果未能解决你的问题,请参考以下文章