项目回归过程中,发现生成的包体积太大了,于是想检查下是那些库类占用的内存过大,并对内存占用过大的文件进行代码优化。优化过程中我们需要知道Link Map File文件(后面会给出Link Map File的解释)。
笔者使用的Shell进行检测,由于脚本文件需要用到字典变量,但是字典变量的声明是Bash Shell版本是4.0以上,所以还需要对Bash Shell版本进行升级。
$ echo $SHELL
/bin/bash
#方式一
$ bash -version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin18)
Copyright (C) 2007 Free Software Foundation, Inc.
#方式二
$ echo $BASH_VERSION
3.2.57(1)-release
# 下载方式一
$ wget ftp://mirrors.ustc.edu.cn/gnu/bash/bash-4.2.tar.gz
# 下载方式二
$ brew install bash
# 解压后会在当前目录下生成一个bash-4.2目录
$ tar zxvf bash-4.2.tar.gz
# 进入目录bash-4.2
$ cd bash-4.2
# 准备配置configure
$ ./configure --prefix=/usr/local/bash4.2
# 编译
$ make
# 安装
$ make install
# 新安装的 Bash shell 为了设置成默认shell,需将其添加到/etc/shells文件。(需root身份编辑)
$ sudo echo "/usr/local/bash4.2/bin/bash" >> /etc/shells
# 设置默认Shell
$ chsh -s /usr/local/bin/bash
# chsh命令仅修改当前用户的默认shell。如果想修改root用户
$ sudo chsh -s /usr/local/bin/bash
由于没有修改Bash的默认版本,而是安装了新版并将其设置为默认。Bash的两个版本并存:
/bin/bash:旧版
/usr/local/bash4.2/bin/bash:新版
#!/bin/bash
echo $BASH_VERSION
注意,这一行Shebang明确指定了旧版本Bash。运行时就会调用旧版本的Bash执行(脚本输出显示,例如3.2.57(1)-release)。
#!//usr/local/bash4.2/bin/bash
echo $BASH_VERSION
现在输出类似4.2.0(1)-release。但要注意,该方案不可移植,可能无法在其他系统上运行。因为其他系统可能没有位于/usr/local/bin/bash的shell(而/bin/bash基本是标准)。
#!/usr/bin/env bash
echo $BASH_VERSION
这是shebang的推荐格式。原理是检查PATH并使用第一个找到的bash可执行文件作为脚本的解释器。如果新版的目录先于旧版(是默认版本),则将使用新版,并且脚本的输出类似4.2.0(1)-release。
Link Map File中文直译为链接映射文件,它是在Xcode生成可执行文件的同时生成的链接信息文件,用于描述可执行文件的构造部分,包括了代码段和数据段的分布情况。Xcode在生成可执行文件的时候默认情况下不生成该文件,需要开发者手动设置Target --> Build Setting --> Write Link Map File为YES:
# Path: /Users/dafyit/Library/Developer/Xcode/DerivedData/DetectTool-gqssxoqjthidklfikbryscecknjr/Build/Products/Debug-iphonesimulator/DetectTool.app/DetectTool
// Path是可执行文件的路径
# Arch: x86_64
// Arch指代架构类型。
# Object files:
[ 0] linker synthesized
[ 1] /Users/dafyit/Library/Developer/Xcode/DerivedData/DetectTool-gqssxoqjthidklfikbryscecknjr/Build/Intermediates.noindex/DetectTool.build/Debug-iphonesimulator/DetectTool.build/DetectTool.app-Simulated.xcent
[ 2] /Users/dafyit/Library/Developer/Xcode/DerivedData/DetectTool-gqssxoqjthidklfikbryscecknjr/Build/Intermediates.noindex/DetectTool.build/Debug-iphonesimulator/DetectTool.build/Objects-normal/x86_64/ViewController.o
[ 3] /Users/dafyit/Library/Developer/Xcode/DerivedData/DetectTool-gqssxoqjthidklfikbryscecknjr/Build/Intermediates.noindex/DetectTool.build/Debug-iphonesimulator/DetectTool.build/Objects-normal/x86_64/AppDelegate.o
[ 4] /Users/dafyit/Library/Developer/Xcode/DerivedData/DetectTool-gqssxoqjthidklfikbryscecknjr/Build/Intermediates.noindex/DetectTool.build/Debug-iphonesimulator/DetectTool.build/Objects-normal/x86_64/main.o
[ 5] /Users/dafyit/Library/Developer/Xcode/DerivedData/DetectTool-gqssxoqjthidklfikbryscecknjr/Build/Intermediates.noindex/DetectTool.build/Debug-iphonesimulator/DetectTool.build/Objects-normal/x86_64/SceneDelegate.o
[ 6] /Users/dafyit/Library/Developer/Xcode/DerivedData/DetectTool-gqssxoqjthidklfikbryscecknjr/Build/Intermediates.noindex/DetectTool.build/Debug-iphonesimulator/DetectTool.build/Objects-normal/x86_64/DetectCodeTool.o
[ 7] /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13.2.sdk/System/Library/Frameworks//Foundation.framework/Foundation.tbd
[ 8] /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13.2.sdk/usr/lib/libobjc.tbd
[ 9] /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13.2.sdk/System/Library/Frameworks//UIKit.framework/UIKit.tbd
// Object Files 可执行文件里所有的obj以及tbd。前面的[0]、[1].....代表对文件的编号。
# Sections:
# Address Size Segment Section
0x100000C80 0x0000073E __TEXT __text
0x1000013BE 0x00000042 __TEXT __stubs
0x100001400 0x0000007E __TEXT __stub_helper
0x10000147E 0x00000DA6 __TEXT __objc_methname
0x100002224 0x0000007F __TEXT __objc_classname
0x1000022A3 0x00000ADA __TEXT __objc_methtype
0x100002D7D 0x000000B2 __TEXT __cstring
0x100002E30 0x0000000A __TEXT __ustring
0x100002E3A 0x0000017A __TEXT __entitlements
0x100002FB4 0x00000048 __TEXT __unwind_info
0x100003000 0x00000018 __DATA_CONST __got
0x100003018 0x000000A0 __DATA_CONST __cfstring
0x1000030B8 0x00000020 __DATA_CONST __objc_classlist
0x1000030D8 0x00000020 __DATA_CONST __objc_protolist
0x1000030F8 0x00000008 __DATA_CONST __objc_imageinfo
0x100004000 0x00000058 __DATA __la_symbol_ptr
0x100004058 0x000013D0 __DATA __objc_const
0x100005428 0x00000048 __DATA __objc_selrefs
0x100005470 0x00000030 __DATA __objc_classrefs
0x1000054A0 0x00000008 __DATA __objc_superrefs
0x1000054A8 0x00000008 __DATA __objc_ivar
0x1000054B0 0x00000140 __DATA __objc_data
0x1000055F0 0x00000188 __DATA __data
// Mach-O文件中的虚拟地址最终会被映射到物理地址上,这些地址会被分为不同的段类型:_ _ TEXT 、_ _ DATA以及_ _ LINKEDIT等。各个段的含义如下:
_ _ TEXT包含了被执行的代码。这些代码是只读、可执行
_ _ DATA包含了包含了将会被更改的数据,例如全局变量、静态变量等,可读写,但是不可执行
_ _ LINKEDIT 包含了加载程序的元数据,比如函数名称和地址,只读。
# Symbols:
# Address Size File Name
0x100000C80 0x00000080 [ 2] -[ViewController viewDidLoad]
0x100000D00 0x00000080 [ 3] -[AppDelegate application:didFinishLaunchingWithOptions:]
0x100000D80 0x00000120 [ 3] -[AppDelegate application:configurationForConnectingSceneSession:options:]
0x100000EA0 0x0000006C [ 3] -[AppDelegate application:didDiscardSceneSessions:]
0x100000F10 0x00000090 [ 4] _main
0x100000FA0 0x000000A0 [ 5] -[SceneDelegate scene:willConnectToSession:options:]
0x100001040 0x00000040 [ 5] -[SceneDelegate sceneDidDisconnect:]
0x100001080 0x00000040 [ 5] -[SceneDelegate sceneDidBecomeActive:]
0x1000010C0 0x00000040 [ 5] -[SceneDelegate sceneWillResignActive:]
0x100001100 0x00000040 [ 5] -[SceneDelegate sceneWillEnterForeground:]
0x100001140 0x00000040 [ 5] -[SceneDelegate sceneDidEnterBackground:]
0x100001180 0x00000020 [ 5] -[SceneDelegate window]
0x1000011A0 0x00000040 [ 5] -[SceneDelegate setWindow:]
0x1000011E0 0x00000033 [ 5] -[SceneDelegate .cxx_destruct]
0x100001220 0x00000190 [ 6] -[DetectCodeTool printSource]
0x1000013B0 0x0000000E [ 6] -[DetectCodeTool cjh_123456]
...
// 根据Sections的起始地址,可以将Symbols分为Sections个数的组,例如0x100001730到0x100001A64之间,就是 _ _ test代码区。
Symbols包含的信息有:
Address:起始地址
Size:所占内存大小,这里使用16进制表示。
File:该Name所在的文件编号,也就是Object files部分的中括号的数字,例如-[ViewController viewDidLoad]对应的文件编号为2,根据Object files部分可以看到所属的文件为:ViewController.o。这样可以计算某个o文件所占内存的大小。只需要把Symbols中编号为o编号Symbols累加统计即可。
Name:就是该Sybmols的名称。
# Dead Stripped Symbols:
# Size File Name
<> 0x00000018 [ 2] CIE
<> 0x00000018 [ 3] CIE
<> 0x00000018 [ 4] CIE
<> 0x00000009 [ 5] literal string: NSObject
<> 0x00000009 [ 5] literal string: isEqual:
<> 0x00000006 [ 5] literal string: class
<> 0x00000005 [ 5] literal string: self
通过分析上述Link Map File文件,可以很容易匹配到各个可执行文件以及类库tbd的大小。
资源脚本链接
http://c.biancheng.net/view/741.html
https://www.jianshu.com/p/905d178f433c
https://www.jianshu.com/p/f003df0d0861