一、获取符号

先把获取符号的代码写在touchBegan里面:

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    //    NSLog(@"%s",__func__);
//    由于不知道有多少个,所有用while循环
    while(YES){
        //        将node取出来
        SYNode *node = OSAtomicDequeue(&symbolList, offsetof(SYNode, next));
        //        取到node为空退出当时循环
        if(node == NULL){
            break;
        }
//        打印拿到符号的信息
        Dl_info info;
        dladdr(node->pc,&info);
        printf("%s\n",info.dli_sname);
    }
} 

点击运转,会打印出一堆的touchesBegan

-[ViewController touchesBegan:withEvent:]
-[ViewController touchesBegan:withEvent:]
-[ViewController touchesBegan:withEvent:]
-[ViewController touchesBegan:withEvent:]
-[ViewController touchesBegan:withEvent:]
-[ViewController touchesBegan:withEvent:]
-[ViewController touchesBegan:withEvent:]
-[ViewController touchesBegan:withEvent:]
-[ViewController touchesBegan:withEvent:]
…

回到Build setting,将本来符号那里增加一个参数func

启动优化clang插桩(三)

再次运转,点击屏幕打印:

-[ViewController touchesBegan:withEvent:]
-[SceneDelegate sceneDidBecomeActive:]
-[SceneDelegate sceneWillEnterForeground:]
-[ViewController viewDidLoad]
-[SceneDelegate window]
-[SceneDelegate scene:willConnectToSession:options:]
-[SceneDelegate window]
-[SceneDelegate setWindow:]
-[SceneDelegate window]
-[AppDelegate application:didFinishLaunchingWithOptions:]
main

这样就拿到了所有的符号。

二、处理符号

由于队列是先进后出,所以咱们需求做一个取反的操作,并且还有一些是重复的符号,咱们需求去掉,处理完这些过程之后的这些符号便是程序启动时分的次序。

先给函数增加下划线:

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    //    NSLog(@"%s",__func__);
    //    初始化一个数组来装载有次序的数据
    NSMutableArray *symbolNames = [NSMutableArray array];
    //由于不知道有多少个,所有用while循环
    while(YES){
        //将node取出来
        SYNode *node = OSAtomicDequeue(&symbolList, offsetof(SYNode, next));
        //        取到node为空退出当时循环
        if(node == NULL){
            break;
        }
        //打印拿到符号的信息
        Dl_info info;
        dladdr(node->pc,&info);
        printf("%s\n",info.dli_sname);
        //转为OC字符
        NSString *name = @(info.dli_sname );
        //判别是否是办法
        BOOL isMethod = [name hasPrefix:@"+["] ||
        [name hasPrefix:@"-["];
        //拿到处理后的符号
        NSString * symbolName = isMethod? name : [@“_” stringByAppendingString:name];
//        增加进数组
        [symbolNames addObject:symbolName];
    }
    NSLog(@"%@",symbolNames);
}

运转打印,得到:

(
    "-[ViewController touchesBegan:withEvent:]",
    "-[SceneDelegate sceneDidBecomeActive:]",
    "-[SceneDelegate sceneWillEnterForeground:]",
    "-[SceneDelegate window]",
    "-[SceneDelegate scene:willConnectToSession:options:]",
    "-[SceneDelegate window]",
    "-[SceneDelegate setWindow:]",
    "-[SceneDelegate window]",
    "-[AppDelegate application:didFinishLaunchingWithOptions:]",
    "_main"
)

这样main函数就加上了下划线“_”

三、符号逆序

直接反向遍历

NSEnumerator *em = [symbolNames reverseObjectEnumerator];
NSLog(@"%@",em.allObjects);

运转打印,得到:

 (
    "_main",
    "-[AppDelegate application:didFinishLaunchingWithOptions:]",
    "-[SceneDelegate window]",
    "-[SceneDelegate setWindow:]",
    "-[SceneDelegate window]",
    "-[SceneDelegate scene:willConnectToSession:options:]",
    "-[SceneDelegate window]",
    "-[ViewController viewDidLoad]",
    "-[SceneDelegate sceneWillEnterForeground:]",
    "-[SceneDelegate sceneDidBecomeActive:]",
    "-[ViewController touchesBegan:withEvent:]"
)

这就得到咱们想要的次序。

四、符号去重

//   初始化去重后的数组
NSMutableArray *funcs = [NSMutableArray arrayWithCapacity:symbolNames.count];
//    定义表明
NSString *name;
//    判别是否数组里现已存在,不存在则增加
while (name = [em nextObject]) {
if(![funcs containsObject:name]){
[funcs addObject:name];
    }
}
NSLog(@"%@",funcs);

运转打印,得到:

(
    "_main",
    "-[AppDelegate application:didFinishLaunchingWithOptions:]",
    "-[SceneDelegate window]",
    "-[SceneDelegate setWindow:]",
    "-[SceneDelegate scene:willConnectToSession:options:]",
    "-[ViewController viewDidLoad]",
    "-[SceneDelegate sceneWillEnterForeground:]",
    "-[SceneDelegate sceneDidBecomeActive:]",
    "-[ViewController touchesBegan:withEvent:]"
)

能够发现里面现已没有了重复的符号

五、生成.order文件

//    拼接成一个字符串
    NSString *funcsStr = [funcs componentsJoinedByString:@"\n"];
//    文件途径
    NSString *filePath = [NSTemporaryDirectory() stringByAppendingString:@"TraceDemo.order"];
//    文件的内容
    NSData *file = [funcsStr dataUsingEncoding:NSUTF8StringEncoding];
//    写入文件
    [[NSFileManager defaultManager] createFileAtPath:filePath contents:file attributes:nil];
//    打印途径
    NSLog(@"%@",NSHomeDirectory());

运转打印:

TraceDemo[31577:752540] /Users/xxxx/Library/Developer/CoreSimulator/Devices/876D0DEB-7AC9-4B67-A877-DB2BC4B5BD10/data/Containers/Data/Application/702BBFFB-D619-4B19-814C-0C9CXXXXX
Tmp文件下能够看到一个.order文件

启动优化clang插桩(三)

翻开文件:

启动优化clang插桩(三)
写入的内容便是咱们想要的内容,这样就能够把.order文件仿制进项目里。

启动优化clang插桩(三)

Order File增加文件方位:

启动优化clang插桩(三)

Link Map File翻开:

启动优化clang插桩(三)

运转,然后找到这个LinkMap文件:

启动优化clang插桩(三)

翻开和.order文件比照:

启动优化clang插桩(三)

发现完全一致。

重排之后削减多少时间,就需求用Instruments工具的System Trace去做具体比照。

六、使用swift情况

假如项目使用swift的话,跟重排和使用OC相似。创建一个swift文件:

import Foundation
class SwiftPage: NSObject{
@objc class public func swiftFunc(){
print("我是swift")
    }
}

导入头文件:

#import "TraceDemo-Swift.h"

增加办法:

- (void)viewDidLoad {
  [super viewDidLoad];
  [SwiftPage swiftFunc];
}

运转:

我是swift

点击屏幕打印:

(
    "_main",
    "-[AppDelegate application:didFinishLaunchingWithOptions:]",
    "-[SceneDelegate window]",
    "-[SceneDelegate setWindow:]",
    "-[SceneDelegate scene:willConnectToSession:options:]",
    "-[ViewController viewDidLoad]",
    "-[SceneDelegate sceneWillEnterForeground:]",
    "-[SceneDelegate sceneDidBecomeActive:]",
    "-[ViewController touchesBegan:withEvent:]"
)

发现并没有打印swift办法,由于swift并不是clang编译的,clang插桩只能编译CC++OC,这里就需求用在Other Swift Flags增加两个符号:-sanitize-coverage=func-sanitize=undefined

启动优化clang插桩(三)

再次运转:

(
    "_main",
    "-[AppDelegate application:didFinishLaunchingWithOptions:]",
    "-[SceneDelegate window]",
    "-[SceneDelegate setWindow:]",
    "-[SceneDelegate scene:willConnectToSession:options:]",
    "-[ViewController viewDidLoad]",
    "_$s9TraceDemo9SwiftPageC9swiftFuncyyFZTo",
    "_$s9TraceDemo9SwiftPageC9swiftFuncyyFZ",
    "_$ss27_finalizeUninitializedArrayySayxGABnlF",
    "_$sSa12_endMutationyyF",
    "_$ss5print_9separator10terminatoryypd_S2StFfA0_",
    "_$ss5print_9separator10terminatoryypd_S2StFfA1_",
    "-[SceneDelegate sceneWillEnterForeground:]",
    "-[SceneDelegate sceneDidBecomeActive:]",
    "-[ViewController touchesBegan:withEvent:]"
)

能够看到swift办法,由于swift办法自带混淆,这里swift也捕获到了,这里就完成了OCswift二进制重排。在项目需求上线的时分,删去一开始的符号-fsanitize-coverage=func,trace-pc-guard和其他测验代码。