iOS調(diào)優(yōu) | 深入理解Link Map File

Link Map File初識

我們編寫的源碼需要經(jīng)過編譯、鏈接叔遂,最終生成一個可執(zhí)行文件他炊。在編譯階段,每個類會生成對應(yīng)的.o文件(目標(biāo)文件)已艰。在鏈接階段痊末,會把.o文件和動態(tài)庫鏈接在一起。Link Map File就是這樣一個記錄鏈接相關(guān)信息的純文本文件哩掺,里面記錄了可執(zhí)行文件的路徑凿叠、CPU架構(gòu)、目標(biāo)文件疮丛、符號等信息幔嫂。

為什么要理解Link Map File

理解Link Map File辆它,可以幫助我們:

  • 理解鏈接過程
  • 理解Crash時,通過Symbols還原出源碼的機(jī)制
  • 理解內(nèi)存分段履恩、分區(qū)
  • 分析可執(zhí)行文件中哪個類或庫占用比較大锰茉,進(jìn)行安裝包瘦身

Link Map File配置

點(diǎn)擊工程,選擇Build Setting選項(xiàng)切心,搜索map飒筑,可以看到如下界面。將Write Link Map File設(shè)置為Yes后绽昏,Build結(jié)束后协屡,會在默認(rèn)路徑下生成一個Link Map File文件,該文件是txt格式的全谤。點(diǎn)擊Path to Link Map File肤晓,可以設(shè)置Debug或Release模式下的生成路徑。


配置

Path & Arch

# Path: /Users/shanggaolin/Library/Developer/Xcode/DerivedData/xxxxxx.app-hjsjojqpqxstlzceepeqbvxqzcrh/Build/Products/Debug-iphonesimulator/xxxxxx.app/xxxxxx
# Arch: x86_64

Path是可執(zhí)行文件的路徑认然,Arch是架構(gòu)類型补憾。

Object files

# Object files:
[  0] linker synthesized
[  1] /Users/shanggaolin/Library/Developer/Xcode/DerivedData/NewMissFresh-hjsjojqpqxstlzceepeqbvxqzcrh/Build/Intermediates.noindex/NewMissFresh.build/Debug-iphonesimulator/NewMissFresh.build/NewMissFresh.app-Simulated.xcent
[  2] /Users/shanggaolin/Library/Developer/Xcode/DerivedData/NewMissFresh-hjsjojqpqxstlzceepeqbvxqzcrh/Build/Intermediates.noindex/NewMissFresh.build/Debug-iphonesimulator/NewMissFresh.build/Objects-normal/x86_64/UIButton+SSEdgeInsets.o
[  3] /Users/shanggaolin/Library/Developer/Xcode/DerivedData/NewMissFresh-hjsjojqpqxstlzceepeqbvxqzcrh/Build/Intermediates.noindex/NewMissFresh.build/Debug-iphonesimulator/NewMissFresh.build/Objects-normal/x86_64/MFRankListTableViewDelegate.o

在編譯成目標(biāo)文件后,通過鏈接器進(jìn)行鏈接卷员,最終合成可執(zhí)行文件盈匾。這里展示的信息是鏈接時用到的文件,包括.o文件和dylib庫毕骡。第一列的序號是類的編號削饵,通過該編號可以對應(yīng)到具體的類。

在后面的Symbols部分未巫,我們會用到類的編號窿撬。

Section區(qū)

# Sections:
# Address   Size        Segment Section
0x100002780 0x0129617D  __TEXT  __text
0x1012988FE 0x000015E4  __TEXT  __stubs
0x101299EE4 0x0000207C  __TEXT  __stub_helper
0x10129BF60 0x0002BE10  __TEXT  __const
0x1012C7D70 0x00097A6D  __TEXT  __objc_methname
0x10135F7DD 0x00010CD5  __TEXT  __objc_classname
0x1013704B2 0x00015F47  __TEXT  __objc_methtype
0x101386400 0x000B6CB2  __TEXT  __cstring
0x10143D0B2 0x00007D60  __TEXT  __ustring
0x101444E14 0x00054EA8  __TEXT  __gcc_except_tab
0x101499CBC 0x000002B0  __TEXT  __entitlements
0x101499F6C 0x00014CA8  __TEXT  __unwind_info
0x1014AEC18 0x0003B3E8  __TEXT  __eh_frame

0x1014EA000 0x00000010  __DATA  __nl_symbol_ptr
0x1014EA010 0x00000BD8  __DATA  __got
0x1014EABE8 0x00001D30  __DATA  __la_symbol_ptr
0x1014EC918 0x00000028  __DATA  __mod_init_func
0x1014EC940 0x00073BB0  __DATA  __const
0x1015604F0 0x00062700  __DATA  __cfstring
0x1015C2BF0 0x00004BD0  __DATA  __objc_classlist
0x1015C77C0 0x00000058  __DATA  __objc_nlclslist
0x1015C7818 0x00000640  __DATA  __objc_catlist
0x1015C7E58 0x00000088  __DATA  __objc_nlcatlist
0x1015C7EE0 0x00001018  __DATA  __objc_protolist
0x1015C8EF8 0x00000008  __DATA  __objc_imageinfo
0x1015C8F00 0x001EE0A0  __DATA  __objc_const
0x1017B6FA0 0x00024A08  __DATA  __objc_selrefs
0x1017DB9A8 0x000000B8  __DATA  __objc_protorefs
0x1017DBA60 0x00004AD8  __DATA  __objc_classrefs
0x1017E0538 0x00002F08  __DATA  __objc_superrefs
0x1017E3440 0x00015A38  __DATA  __objc_ivar
0x1017F8E78 0x0002F670  __DATA  __objc_data
0x1018284F0 0x00024D18  __DATA  __data
0x10184D210 0x00006C74  __DATA  __bss
0x101853E90 0x00000E90  __DATA  __common

第一列是起始位置,第二列是Section占用內(nèi)存大小橱赠,第三列是Segment類型尤仍,第四列是Section類型。
為了理解上面的信息狭姨,我們需要先補(bǔ)充一點(diǎn)Mach-O知識宰啦。
Mach-O 文件中的虛擬地址最終會映射到物理地址上。這些地址被分成不同的Segement: __TEXT段饼拍、__DATA段 和 __LINKEDIT段赡模。
(1)__TEXT 包含 Mach header,被執(zhí)行的代碼和只讀常量(如C 字符串)师抄,只讀可執(zhí)行(r-x)漓柑。
(2)__DATA 包含全局變量,靜態(tài)變量等,可讀寫(rw-)辆布。
(3)__LINKEDIT 包含了加載程序的『元數(shù)據(jù)』瞬矩,比如函數(shù)的名稱和地址,只讀(r–)锋玲。

Segement劃分成了不同的Section景用,不同的Section存儲著不同的信息,下面是一些常用的Section的介紹惭蹂。

//  __TEXT段中的一些Section
1. __text: 代碼節(jié)伞插,存放機(jī)器編譯后的代碼
2. __stubs: 用于輔助做動態(tài)鏈接代碼(dyld).
3. __stub_helper:用于輔助做動態(tài)鏈接(dyld).
4. __objc_methname:objc的方法名稱
5. __cstring:代碼運(yùn)行中包含的字符串常量,比如代碼中定義`#define kGeTuiPushAESKey        @"DWE2#@e2!"`,那DWE2#@e2!會存在這個區(qū)里。
6. __objc_classname:objc類名
7. __objc_methtype:objc方法類型
8. __ustring:
9. __gcc_except_tab:
10. __const:存儲const修飾的常量
11. __dof_RACSignal:
12. __dof_RACCompou:
13. __unwind_info:

// __DATA段中的一些Section
1. __got:存儲引用符號的實(shí)際地址盾碗,類似于動態(tài)符號表
2. __la_symbol_ptr:lazy symbol pointers媚污。懶加載的函數(shù)指針地址。和__stubs和stub_helper配合使用廷雅。具體原理暫留耗美。
3. __mod_init_func:模塊初始化的方法。
4. __const:存儲constant常量的數(shù)據(jù)榜轿。比如使用extern導(dǎo)出的const修飾的常量幽歼。
5. __cfstring:使用Core Foundation字符串
6. __objc_classlist:objc類列表,保存類信息,映射了__objc_data的地址
7. __objc_nlclslist:Objective-C 的 +load 函數(shù)列表谬盐,比 __mod_init_func 更早執(zhí)行。
8. __objc_catlist: categories
9. __objc_nlcatlist:Objective-C 的categories的 +load函數(shù)列表诚些。
10. __objc_protolist:objc協(xié)議列表
11. __objc_imageinfo:objc鏡像信息
12. __objc_const:objc常量飞傀。保存objc_classdata結(jié)構(gòu)體數(shù)據(jù)。用于映射類相關(guān)數(shù)據(jù)的地址诬烹,比如類名砸烦,方法名等。
13. __objc_selrefs:引用到的objc方法
14. __objc_protorefs:引用到的objc協(xié)議
15. __objc_classrefs:引用到的objc類
16. __objc_superrefs:objc超類引用
17. __objc_ivar:objc ivar指針,存儲屬性绞吁。
18. __objc_data:objc的數(shù)據(jù)幢痘。用于保存類需要的數(shù)據(jù)。最主要的內(nèi)容是映射__objc_const地址家破,用于找到類的相關(guān)數(shù)據(jù)颜说。
19. __data:暫時沒理解,從日志看存放了協(xié)議和一些固定了地址(已經(jīng)初始化)的靜態(tài)量汰聋。
20. __bss:存儲未初始化的靜態(tài)量门粪。比如:`static NSThread *_networkRequestThread = nil;`其中這里面的size表示應(yīng)用運(yùn)行占用的內(nèi)存,不是實(shí)際的占用空間烹困。所以計(jì)算大小的時候應(yīng)該去掉這部分?jǐn)?shù)據(jù)玄妈。
21. __common:存儲導(dǎo)出的全局的數(shù)據(jù)。類似于static,但是沒有用static修飾拟蜻。比如KSCrash里面`NSDictionary* g_registerOrders;`, g_registerOrders就存儲在__common里面

虛擬內(nèi)存最終會映射到物理內(nèi)存绎签,通過上面的介紹,我們就可以知道代碼和數(shù)據(jù)在內(nèi)存中是如何存儲的酝锅。

Symbols

<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

# Symbols:
// __text代碼區(qū)
# Address   Size        File  Name
0x100002780 0x00000450  [  2] -[UIButton(SSEdgeInsets) setImageUpTitleDownWithSpacing:]
0x100002BD0 0x00000070  [  2] _UIEdgeInsetsMake
0x100002C40 0x000004B0  [  2] -[UIButton(SSEdgeInsets) setImageRightTitleLeftWithSpacing:]
0x1000030F0 0x000001B0  [  2] -[UIButton(SSEdgeInsets) setDefaultImageTitleStyleWithSpacing:]
0x1000032A0 0x000006F0  [  2] -[UIButton(SSEdgeInsets) setEdgeInsetsWithType:marginType:margin:]
0x100003990 0x000000B0  [  3] -[MFRankListTableViewDelegate removeAllCellModels]
0x100003A40 0x000002A0  [  3] -[MFRankListTableViewDelegate appendCellModels:]
0x100003CE0 0x00000050  [  3] -[MFRankListTableViewDelegate numberOfSectionsInTableView:]
0x100003D30 0x000000C0  [  3] -[MFRankListTableViewDelegate tableView:numberOfRowsInSection:]

<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

// __objc_methname方法名區(qū)
0x1012C7D70 0x0000000F  [  2] literal string: imageForState:
0x1012C7D7F 0x00000005  [  2] literal string: size
0x1012C7D84 0x00000014  [  2] literal string: setTitleEdgeInsets:
0x1012C7D98 0x0000000F  [  2] literal string: titleForState:
0x1012C7DA7 0x00000007  [  2] literal string: length
0x1012C7DAE 0x0000000B  [  2] literal string: titleLabel
0x1012C7DB9 0x00000005  [  2] literal string: font
0x1012C7DBE 0x00000025  [  2] literal string: dictionaryWithObjects:forKeys:count:
0x1012C7DE3 0x00000014  [  2] literal string: sizeWithAttributes:
0x1012C7DF7 0x00000014  [  2] literal string: setImageEdgeInsets:
0x1012C7E0B 0x00000019  [  2] literal string: attributedTitleForState:
0x1012C7E24 0x00000006  [  2] literal string: frame
0x1012C7E2A 0x00000020  [  2] literal string: setImageUpTitleDownWithSpacing:
0x1012C7E4A 0x00000023  [  2] literal string: setImageRightTitleLeftWithSpacing:
0x1012C7E6D 0x00000026  [  2] literal string: setDefaultImageTitleStyleWithSpacing:
0x1012C7E93 0x00000029  [  2] literal string: setEdgeInsetsWithType:marginType:margin:
0x1012C7EBC 0x0000000E  [  3] literal string: setIsLoading:

<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

// __objc_classlist類列表區(qū)
0x1015C2BF0 0x00000008  [  3] anon
0x1015C2BF8 0x00000008  [  4] anon
0x1015C2C00 0x00000008  [  5] anon
0x1015C2C08 0x00000008  [  6] anon
0x1015C2C10 0x00000008  [  7] anon
1辜御、__text代碼區(qū)

通過地址0x100002780,可以知道它位于__TEXT段的__text區(qū)屈张,這段區(qū)域存儲著代碼擒权,通過符號表,根據(jù)地址可以對應(yīng)出源代碼阁谆,如-[UIButton(SSEdgeInsets) setImageUpTitleDownWithSpacing:]碳抄。通過第三列的類編號,可以知道該代碼屬于UIButton+SSEdgeInsets分類场绿。

2剖效、__objc_methname方法名區(qū)

通過地址0x1012C7D70,可以知道它位于__TEXT段的__objc_methname區(qū)焰盗,這段區(qū)域存儲著方法名璧尸,通過符號表,根據(jù)地址可以對應(yīng)出具體的方法名熬拒,如imageForState爷光。
由上面的信息,可以看出方法名越長澎粟,最終占用的內(nèi)存也越大蛀序。

3、__objc_classlist類列表區(qū)

__objc_classlist區(qū)的size值都是8活烙,區(qū)域里存儲的值都是一個指針徐裸,指向了類的虛擬地址(__objc_data區(qū)中地址)。
objc_class的數(shù)據(jù)結(jié)構(gòu)為:

typedef struct objc_class {
        unsigned long long isa;
        unsigned long long wuperclass;
        unsigned long long cache;
        unsigned long long vtable;
        unsigned long long data;
        unsigned long long reserved1;
        unsigned long long reserved2;
        unsigned long long reserved3;
}objc_class;

上面的data字段保存了_objc_const區(qū)對應(yīng)的數(shù)據(jù)地址(objc_classdata地址)啸盏。
objc_classdata數(shù)據(jù)結(jié)構(gòu)為:

typedef struct objc_classdata {
    long long flags;
    long long instanceStart;
    long long instanceSize;
    long long reserved;
    unsigned long long ivarlayout;
    unsigned long long name;
    unsigned long long baseMethod;
    unsigned long long baseProtocol;
    unsigned long long ivars;
    unsigned long long weakIvarLayout;
    unsigned long long baseProperties;
}

objc_classdata結(jié)構(gòu)體中保存了類名重贺,方法名,協(xié)議名回懦,ivar指針和屬性對應(yīng)的地址气笙。通過地址,可以找到對應(yīng)的段和分區(qū)粉怕,進(jìn)而找到對應(yīng)類的各種信息健民。

查找未使用的類和方法

__objc_classrefs是引用到的類,_objc_classname是所有類名贫贝,通過分析兩者之間的差別秉犹,就可以知道哪些類沒有用到蛉谜。
同理,分析__objc_selrefs和_objc_methname的差別崇堵,也可知道哪些方法沒用到型诚。
由于OC是動態(tài)語言,中間可能會出現(xiàn)判斷失誤的情況鸳劳。

分析大文件

在Symbols部分狰贯,我們可以把類編號相同的size加起來,算出每個類或庫占用的大小赏廓。在Object files部分根據(jù)類的編號可以查出對應(yīng)的類涵紊。分析的結(jié)果對App安裝包瘦身會有一些幫助。github上有很多實(shí)現(xiàn)了這樣功能的開源工具幔摸,實(shí)現(xiàn)原理很簡單摸柄,如有需要可自行查找。

參考文獻(xiàn)

優(yōu)化 App 的啟動時間

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末既忆,一起剝皮案震驚了整個濱河市驱负,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌患雇,老刑警劉巖跃脊,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異苛吱,居然都是意外死亡酪术,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門又谋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來拼缝,“玉大人,你說我怎么就攤上這事彰亥。” “怎么了衰齐?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵任斋,是天一觀的道長。 經(jīng)常有香客問我耻涛,道長废酷,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任抹缕,我火速辦了婚禮澈蟆,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘卓研。我一直安慰自己趴俘,他們只是感情好睹簇,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著寥闪,像睡著了一般太惠。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上疲憋,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天凿渊,我揣著相機(jī)與錄音,去河邊找鬼缚柳。 笑死埃脏,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的秋忙。 我是一名探鬼主播彩掐,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼翰绊!你這毒婦竟也來了佩谷?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤监嗜,失蹤者是張志新(化名)和其女友劉穎谐檀,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體裁奇,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡桐猬,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了刽肠。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片溃肪。...
    茶點(diǎn)故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖音五,靈堂內(nèi)的尸體忽然破棺而出惫撰,到底是詐尸還是另有隱情,我是刑警寧澤躺涝,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布厨钻,位于F島的核電站,受9級特大地震影響坚嗜,放射性物質(zhì)發(fā)生泄漏夯膀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一苍蔬、第九天 我趴在偏房一處隱蔽的房頂上張望诱建。 院中可真熱鬧,春花似錦碟绑、人聲如沸俺猿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽辜荠。三九已至汽抚,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間伯病,已是汗流浹背造烁。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留午笛,地道東北人惭蟋。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像药磺,于是被迫代替她去往敵國和親告组。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評論 2 353

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