啟動(dòng)優(yōu)化 二進(jìn)制重排

一永票、啟動(dòng)優(yōu)化

冷啟動(dòng):第一次啟動(dòng)App
熱啟動(dòng)

啟動(dòng)優(yōu)化一般講的是冷啟動(dòng)

啟動(dòng)階段:main函數(shù)之前、main函數(shù)之后

main 階段:
1、懶加載
2钝的、發(fā)揮CPU的價(jià)值(多線程進(jìn)行初始化)
3惧辈、啟動(dòng)時(shí)避免使用Xib琳状、stroyboard

階段一、main函數(shù)之前

打印啟動(dòng)時(shí)間
  • 添加 DYLD_PRINT_STATISTICS
  • dylib loading time: 加載動(dòng)態(tài)庫(kù) (優(yōu)化:建議不要大于6個(gè))
  • rebase/binding time: 修正內(nèi)部偏移指針/外部符號(hào)綁定 (優(yōu)化:減少OC類) 優(yōu)化少
  • ObjC setup time: OC類的注冊(cè) (優(yōu)化:減少OC類) 優(yōu)化少
  • initializer time: 加載load方法 (優(yōu)化:使用懶加載)

階段二盒齿、main函數(shù)之后

main 開(kāi)始 到 第一個(gè)界面念逞。

打點(diǎn)困食,使用BLStopwatch.h和BLStopwatch.m這個(gè)類


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    [[BLStopwatch sharedStopwatch] start];
    int a = 0;
    for (int i = 0; i < 10000000; i++) {
        a++;
    }
    [[BLStopwatch sharedStopwatch] splitWithDescription:@"didFinishLaunchingWithOptions"];
    
    return YES;
}
- (void)viewDidLoad {
    [super viewDidLoad];
    //刷新時(shí)間:
    [[BLStopwatch sharedStopwatch] refreshMedianTime];
    
    int a = 0;
    for (int i = 0; i < 10000000; i++) {
        a++;
    };
    [[BLStopwatch sharedStopwatch] splitWithDescription:@"viewDidLoad"];
    
}
-(void)viewDidAppear:(BOOL)animated{
    [super viewDidAppear:animated];
    //刷新時(shí)間:
    [[BLStopwatch sharedStopwatch] refreshMedianTime];
       
    int a = 0;
    for (int i = 0; i < 10000000; i++) {
        a++;
    };
    [[BLStopwatch sharedStopwatch] splitWithDescription:@"viewDidAppear"];
    [[BLStopwatch sharedStopwatch] stopAndPresentResultsThenReset];
    
}

二庵楷、二進(jìn)制重排

二進(jìn)制重排是在main函數(shù)之前

物理內(nèi)存
虛擬內(nèi)存 : 解決安全問(wèn)題塘匣、解決內(nèi)存使用率問(wèn)題

  • 解決安全問(wèn)題:映射表(頁(yè)表)(虛擬頁(yè)表)
  • 解決內(nèi)存使用率問(wèn)題:內(nèi)存分頁(yè)管理。缺頁(yè)中斷祈坠,然后加載到物理內(nèi)存叨咖,加載之前會(huì)簽名加載的頁(yè)瘩例;如果啟動(dòng)的時(shí)候要加載的代碼分別在不同的頁(yè),那么缺頁(yè)中斷時(shí)間就比較長(zhǎng)甸各,這時(shí)就出現(xiàn)了二進(jìn)制重排(把啟動(dòng)要加載的代碼放在前面幾頁(yè))垛贤。使用內(nèi)存分頁(yè)后,就會(huì)導(dǎo)致代碼的加載都是從0開(kāi)始的趣倾,為了防止黑客聘惦,就出現(xiàn)了ASLR。

內(nèi)存分頁(yè)技術(shù)
MacOS 儒恋、linux (4K為一頁(yè))
iOS(16K為一頁(yè))

PageFault(缺頁(yè)中斷)

1善绎、command+I

2、選擇System Trace

3碧浊、點(diǎn)擊一下涂邀,第一個(gè)頁(yè)面出現(xiàn)后,再點(diǎn)擊一下

4箱锐、搜索Main Thread

5比勉、選擇Main Thread、選擇Virtual Memory驹止。File Backed Page in 就是PageFault

二進(jìn)制重排優(yōu)化是在鏈接階段對(duì)即將生成的可執(zhí)行文件進(jìn)行重新排列

order_file

1浩聋、打開(kāi)objc4-750源碼


libobjc.order存放的是方法的調(diào)用順序,可以用終端cat打開(kāi)

2臊恋、Build Settings中搜索order file

load方法的執(zhí)行順序

生成LinkMap文件

  • Build Settings 中搜索 link map,No改為Yes衣洁,然后Command+B,build一下,就會(huì)生成LinkMap文件

打開(kāi)LinkMap文件

  • Address: 函數(shù)真實(shí)實(shí)現(xiàn)的地址(匯編代碼的地址)(代碼的地址)
  • Size:函數(shù)的大小抖仅,寫(xiě)的代碼的多與少
  • File:所在文件
  • Name: 方法名


  • 0x0000000100d30000(ASLR)+00004848(偏移) = 0x100d34848


  • +[ViewController load]方法里面打斷點(diǎn)坊夫,看到的地址和0x0000000100d30000(ASLR)+00004848(偏移) = 0x100d34848相等,dis -s 0x100d34848查看匯編代碼
  • 0x100d34848撤卢、0x100d3484c环凿、0x100d34850,這些是代碼的地址

添加dyz.order文件

  • 在項(xiàng)目的根目錄創(chuàng)建一個(gè).order文件放吩,寫(xiě)入如下代碼


  • Build Settings中搜索order file智听,添加dyz.order文件的地址(./dyz.order或者${SRCROOT}/dyz.order)
  • command + shift + k清空一下緩存,command+B編譯一下
  • 再次查看LinkMap文件


Clang插莊

  • Build Settings 搜索 other c flags,添加-fsanitize-coverage=trace-pc-guard參數(shù)
  • 粘貼如下代碼到項(xiàng)目
extern "C" 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);
[圖片上傳中...(Snip20200420_13.png-54b663-1587378588681-0)]
  for (uint32_t *x = start; x < stop; x++)
    *x = ++N;  // Guards should start from 1.
}

extern "C" void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {
  if (!*guard) return;  // Duplicate the guard check.
  // If you set *guard to 0 this code will not be called again for this edge.
  // Now you can get the PC and do whatever you want:
  //   store it somewhere or symbolize it and print right away.
  // The values of `*guard` are as you set them in
  // __sanitizer_cov_trace_pc_guard_init and so you can make them consecutive
  // and use them to dereference an array or a bit vector.
  void *PC = __builtin_return_address(0);
  char PcDescr[1024];
  // This function is a part of the sanitizer run-time.
  // To use it, link with AddressSanitizer or other sanitizer.
  __sanitizer_symbolize_pc(PC, "%p %F %L", PcDescr, sizeof(PcDescr));
  printf("guard: %p %x PC %s\n", guard, *guard, PcDescr);
}
  • 調(diào)試
    暫停到推,然后附加考赛,然后x讀start內(nèi)存


    讀頭 x 0x10d4d7490
    讀尾 x 0x10d4d74c8-0x4,讀尾需要減四個(gè)字節(jié)莉测,因?yàn)橐粋€(gè)方法占四個(gè)字節(jié)颜骤,而這個(gè)0x10d4d74c8地址是尾方法結(jié)束的地址,所以如果需要讀尾方法的地址悔雹,就需要減掉四個(gè)字節(jié)

  • 靜態(tài)插莊复哆!
    所有方法、函數(shù)腌零、block 內(nèi)部都加入一行代碼梯找!調(diào)用 __sanitizer_cov_trace_pc_guard

  • dis -s 0x00000001043e9a54

  • dis -s 0x00000001043e9a54-116 減去偏移值

  • 拿到所有符號(hào)。導(dǎo)入dlfcn.h頭文件

#import "ViewController.h"
#import <dlfcn.h>

@interface ViewController ()

@end

@implementation ViewController

+(void)initialize
{
    
}

void(^block1)(void) = ^(void) {
    
};

void test(){
    block1();
    
}

+(void)load
{
    
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    
}

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) {
  if (!*guard) return;  // Duplicate the guard check.
  void *PC = __builtin_return_address(0);
    Dl_info info;
    dladdr(PC, &info);
    printf("dli_fname:%s \n dli_fbase:%p \n dli_sname:%s \n dli_saddr:%p \n",info.dli_fname,info.dli_fbase,info.dli_sname,info.dli_saddr);
    
//  char PcDescr[1024];
//  __sanitizer_symbolize_pc(PC, "%p %F %L", PcDescr, sizeof(PcDescr));
//  printf("guard: %p %x PC %s\n", guard, *guard, PcDescr);
}

@end
  • while循環(huán)益涧,中會(huì)調(diào)用__sanitizer_cov_trace_pc_guard锈锤,會(huì)導(dǎo)致一直打印-[ViewController touchesBegan:withEvent:]
#import "ViewController.h"
#import <dlfcn.h>
#import <libkern/OSAtomic.h>

@interface ViewController ()

@end

@implementation ViewController

+(void)initialize
{
    
}

void(^block1)(void) = ^(void) {
    
};

void test(){
    block1();
    
}

+(void)load
{
    
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    while (YES) {
        SYNode * node = OSAtomicDequeue(&symbolList, offsetof(SYNode, next));
        if (node == NULL) {
            break;
        }
        Dl_info info;
        dladdr(node->pc, &info);
        printf("%s \n",info.dli_sname);
    }
}

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.
}

//原子隊(duì)列
static  OSQueueHead symbolList = OS_ATOMIC_QUEUE_INIT;
//定義符號(hào)結(jié)構(gòu)體
typedef struct {
    void *pc;
    void *next;
}SYNode;

void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {
    if (!*guard) return;  // Duplicate the guard check.
    void *PC = __builtin_return_address(0);
    SYNode *node = malloc(sizeof(SYNode));
    *node = (SYNode){PC,NULL};
    //進(jìn)入
    OSAtomicEnqueue(&symbolList, node, offsetof(SYNode, next));
}

@end


解決方法 Other C Flags的參數(shù),等號(hào)后面加上func,例如-fsanitize-coverage=func,trace-pc-guard

  • 取反闲询、去重久免、函數(shù)符號(hào)前面添加下劃線(除了OC方法)、添加load
  • 添加load:注銷__sanitizer_cov_trace_pc_guard中的if (!*guard) return;,load的守衛(wèi)是0
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    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 * emt = [symbolNames reverseObjectEnumerator];
    //去重
    NSMutableArray<NSString *> *funcs = [NSMutableArray arrayWithCapacity:symbolNames.count];
    NSString * name;
    while (name = [emt nextObject]) {
        if (![funcs containsObject:name]) {
            [funcs addObject:name];
        }
    }
    //干掉自己!
    [funcs removeObject:[NSString stringWithFormat:@"%s",__FUNCTION__]];
    //將數(shù)組變成字符串
    NSString * funcStr = [funcs  componentsJoinedByString:@"\n"];
    
    NSString * filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"dyz.order"];
    NSData * fileContents = [funcStr dataUsingEncoding:NSUTF8StringEncoding];
    [[NSFileManager defaultManager] createFileAtPath:filePath contents:fileContents attributes:nil];
    NSLog(@"%@",filePath);
}
  • 下載下來(lái)扭弧,顯示包內(nèi)容




  • 項(xiàng)目中如果是OC和Swift混編
    1阎姥、在Build Settings搜索other swift flags,如果是OC項(xiàng)目鸽捻,里面沒(méi)有Swift文件呼巴,那么搜索不到Other Swift Flags,只有項(xiàng)目里面有了Swift文件才會(huì)搜索到御蒲。



    2衣赶、添加參數(shù)-sanitize-coverage=func-sanitize=undefined

    3、導(dǎo)入Swift頭文件#import "TraceDemo-Swift.h"厚满,調(diào)用Swift方法[SwiftTest swiftTestLoad];

    4府瞄、Swift方法同樣可以hook到

最后

  • 把得到的dyz.order文件拷貝到項(xiàng)目的根目錄下。
  • Build Settings中搜索order file碘箍,添加dyz.order文件的地址(./dyz.order或者${SRCROOT}/dyz.order)
  • Build Settings 中搜索 link map,如果是Yes則改回No
  • 去掉 Other C Flags的參數(shù) -fsanitize-coverage=func,trace-pc-guard
  • 去掉 Other Swift Flags的參數(shù) -sanitize-coverage=func-sanitize=undefined
  • 注銷__sanitizer_cov_trace_pc_guard_init和__sanitizer_cov_trace_pc_guard方法
  • 結(jié)束遵馆,打包上線
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市丰榴,隨后出現(xiàn)的幾起案子团搞,更是在濱河造成了極大的恐慌,老刑警劉巖多艇,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異像吻,居然都是意外死亡峻黍,警方通過(guò)查閱死者的電腦和手機(jī)复隆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)姆涩,“玉大人挽拂,你說(shuō)我怎么就攤上這事」嵌觯” “怎么了亏栈?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)宏赘。 經(jīng)常有香客問(wèn)我绒北,道長(zhǎng),這世上最難降的妖魔是什么察署? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任闷游,我火速辦了婚禮,結(jié)果婚禮上贴汪,老公的妹妹穿的比我還像新娘脐往。我一直安慰自己,他們只是感情好扳埂,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布业簿。 她就那樣靜靜地躺著,像睡著了一般阳懂。 火紅的嫁衣襯著肌膚如雪梅尤。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,031評(píng)論 1 285
  • 那天希太,我揣著相機(jī)與錄音克饶,去河邊找鬼。 笑死誊辉,一個(gè)胖子當(dāng)著我的面吹牛矾湃,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播堕澄,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼邀跃,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了蛙紫?” 一聲冷哼從身側(cè)響起拍屑,我...
    開(kāi)封第一講書(shū)人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎坑傅,沒(méi)想到半個(gè)月后僵驰,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年蒜茴,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了星爪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡粉私,死狀恐怖顽腾,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情诺核,我是刑警寧澤抄肖,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站窖杀,受9級(jí)特大地震影響漓摩,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜陈瘦,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一幌甘、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧痊项,春花似錦锅风、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至咖驮,卻和暖如春边器,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背托修。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工忘巧, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人睦刃。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓砚嘴,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親涩拙。 傳聞我的和親對(duì)象是個(gè)殘疾皇子际长,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容