一、LinkMap文件分析

说明:LinkMap数据是根据文章《LinkMap文件分析》中方法实验实测数据。

如何获得LinkMap文件
1.在XCode中开启编译选项Write Link Map File \n
XCode -> Project -> Build Settings -> 把Write Link Map File选项设为yes,并指定好linkMap的存储位置
2.工程编译完成后,在编译目录里找到Link Map文件(txt类型)
在Build Settings里Path to Link Map File对应Link Map的路径,以下是默认路径:
/Users/xxx/Library/Developer/Xcode/DerivedData/YourApp/Build/Intermediates.noindex/YourApp.build/Debug-iphoneos/YourApp.build/YourApp-LinkMap-normal-arm64.txt

LinkMap里有了每个目标文件每个方法每个数据的占用大小数据,所以只要写个脚本,就可以统计出每个.o最后的大小,属于一个.a静态链接库的.o加起来,就是这个库在APP里占用的空间大小。

LinkMap包含以下部分:

# Path: .../*.app文件
# Arch: arm64
# Object files: .../*.o文件
# Sections: Text和Data分类大小
# Symbols: 与Object files对应起来,是Object files的详细Symbal信息,包括地址和大小
# Dead Stripped Symbols:
// linkmap.js
var readline = require('readline'),
fs = require('fs'); var LinkMap = function(filePath) {
this.files = []
this.filePath = filePath
} LinkMap.prototype = {
start: function(cb) {
var self = this
var rl = readline.createInterface({
input: fs.createReadStream(self.filePath),
output: process.stdout,
terminal: false
});
var currParser = "";
rl.on('line', function(line) {
if (line[0] == '#') {
if (line.indexOf('Object files') > -1) {
currParser = "_parseFiles";
} else if (line.indexOf('Sections') > -1) {
currParser = "_parseSection";
} else if (line.indexOf('Symbols') > -1) {
currParser = "_parseSymbols";
}
return;
}
if (self[currParser]) {
self[currParser](line)
}
}); rl.on('close', function(line) {
cb(self)
});
}, _parseFiles: function(line) {
var arr =line.split(']')
if (arr.length > 1) {
var idx = Number(arr[0].replace('[',''));
var file = arr[1].split('/').pop().trim()
this.files[idx] = {
name: file,
size: 0
}
}
}, _parseSection: function(line) {
}, _parseSymbols: function(line) {
var arr = line.split('\t')
if (arr.length > 2) {
var size = parseInt(arr[1], 16)
var idx = Number(arr[2].split(']')[0].replace('[', ''))
if (idx && this.files[idx]) {
this.files[idx].size += size;
}
}
}, _formatSize: function(size) {
if (size > 1024 * 1024) return (size/(1024*1024)).toFixed(2) + "MB"
if (size > 1024) return (size/1024).toFixed(2) + "KB"
return size + "B"
}, statLibs: function(h) {
var libs = {}
var files = this.files;
var self = this;
for (var i in files) {
var file = files[i]
var libName
if (file.name.indexOf('.o)') > -1) {
libName = file.name.split('(')[0]
} else {
libName = file.name
}
if (!libs[libName]) {
libs[libName] = 0
}
libs[libName] += file.size
}
var i = 0, sortLibs = []
for (var name in libs) {
sortLibs[i++] = {
name: name,
size: libs[name]
}
}
sortLibs.sort(function(a,b) {
return a.size > b.size ? -1: 1
})
if (h) {
sortLibs.map(function(o) {
o.size = self._formatSize(o.size)
})
}
return sortLibs
}, statFiles: function(h) {
var self = this
self.files.sort(function(a,b) {
return a.size > b.size ? -1: 1
})
if (h) {
self.files.map(function(o) {
o.size = self._formatSize(o.size)
})
}
return this.files
}
} if (!process.argv[2]) {
console.log('usage: node linkmap.js filepath -hl')
console.log('-h: format size')
console.log('-l: stat libs')
return
}
var isStatLib, isFomatSize
var opts = process.argv[3];
if (opts && opts[0] == '-') {
if (opts.indexOf('h') > -1) isFomatSize = true
if (opts.indexOf('l') > -1) isStatLib = true
} var linkmap = new LinkMap(process.argv[2])
linkmap.start(function(){
var ret = isStatLib ? linkmap.statLibs(isFomatSize)
: linkmap.statFiles(isFomatSize)
for (var i in ret) {
console.log(ret[i].name + '\t' + ret[i].size)
}
})

执行:

node linkmap.js LinkMap-normal-arm64.txt -hl

得到:

YourSDK(YourSDK-arm64-master.o)    122.76KB

二、xcodebuild总包大小分析

分析:Release 包(arm64)为什么比debug包(x86_64)大那么多?

分析步骤

1.  抽出YourSDK的arm64看包大小(1.6M)

lipo  -thin arm64 -output YourSDK_arm64.a YourSDK.framework/YourSDK
➜ ls -al YourSDK_arm64.a
-rw-r--r-- 1 yushu.lxy staff 1606208 11 18 15:58 YourSDK_arm64.a

2.  整体

size YourSDK_armv64.a
__TEXT __DATA __OBJC others dec hex
86591 39492 0 1190617 1316700 14175c YourSDK_arm64.a(YourSDK-arm64-master.o)
30 240 0 4566 4836 12e4 YourSDK_arm64.a(libPods-YourSDK.a-arm64-master.o)

查看.a文件下.o文件大小

ar -t -v YourSDK_arm64.a
rw-r--r-- 501/20 5776 Nov 18 15:58 2019 __.SYMDEF SORTED
rw-r--r-- 0/0 1593168 Jan 1 08:00 1970 YourSDK-arm64-master.o
rw-r--r-- 0/0 6976 Jan 1 08:00 1970 libPods-YourSDK.a-arm64-master.o

3.  详细

size -m YourSDK_arm64.a
YourSDK_arm64.a(YourSDK-arm64-master.o):
Segment : 1316713
Section (__TEXT, __text): 68676(比debug的包小)
Section (__TEXT, __objc_methname): 8393
Section (__TEXT, __cstring): 5091
Section (__TEXT, __objc_classname): 1636
Section (__TEXT, __objc_methtype): 1937
Section (__TEXT, __literal16): 128(只有debug包有)
Section (__TEXT, __literal4): 8(只有debug包有)
Section (__TEXT, __literal8): 144(比debug的包小)
Section (__TEXT, __eh_frame): 16480
Section (__TEXT, __gcc_except_tab): 608
Section (__TEXT, __ustring): 42
Section (__TEXT, __const): 64
Section (__DATA, __const): 1560
Section (__DATA, __cfstring): 4736
Section (__DATA, __objc_classlist): 400
Section (__DATA, __objc_catlist): 8
Section (__DATA, __objc_protolist): 56
Section (__DATA, __objc_imageinfo): 8
Section (__DATA, __objc_const): 24224
Section (__DATA, __objc_selrefs): 2752
Section (__DATA, __objc_protorefs): 8
Section (__DATA, __objc_classrefs): 592
Section (__DATA, __objc_superrefs): 192
Section (__DATA, __objc_ivar): 252(比debug的包小)
Section (__DATA, __objc_data): 4000
Section (__DATA, __data): 672
Section (__DATA, __bss): 32
Section (__LD, __compact_unwind): 11424
Section (__LLVM, __bundle): 1179193(+)
total 1316700
total 1316713
YourSDK_arm64.a(libPods-YourSDK.a-arm64-master.o):
Segment : 4838
Section (__TEXT, __text): 0
Section (__TEXT, __objc_classname): 30
Section (__DATA, __objc_classlist): 8
Section (__DATA, __objc_imageinfo): 8
Section (__DATA, __objc_const): 144
Section (__DATA, __objc_data): 80
Section (__LLVM, __bitcode): 4480 (+)
Section (__LLVM, __cmdline): 86 (+)
total 4836
total 4838

YourSDK SDK实际大小应该是:1316713-1179193 = 137KB(与LinkMap分析接近)

分析:

上面段数据LLVM bundle部分是release包比debug包大的原因,是因为release包包含了bitCode数据。

参考《关于bitcode, 知道这些就够了》

(1)bitcode是源码和机器码之间的中间形式。

(2)为什么苹果要强推bitcode?开发者把bitcode提交到App Store Connect之后,如果苹果发布了使用新芯片的iPhone,支持更高效的指令,开发者不需要做任何操作,App Store Connect自己就可以编译出针对新产品优化过的app并通过App Store分发给用户,不需要开发者自己重新打包上架。

(3)苹果可以将bitcode代码进行一些逻辑等价的转换,使得代码的执行效率更高,体积更小。

实验:APP的project开启bitcode和关闭bitcode,查看手机中app的实际大小,开启bitcode的包更小。

size -m YourSDK_arm64.a
YourSDK_arm64.a(YourSDK-arm64-master.o):
Segment : 943490
Section (__TEXT, __text): 53184
Section (__TEXT, __objc_classname): 633
Section (__TEXT, __objc_methname): 7919
Section (__TEXT, __objc_methtype): 850
Section (__TEXT, __cstring): 7352
Section (__TEXT, __literal8): 64
Section (__TEXT, __gcc_except_tab): 1724
Section (__TEXT, __const): 112
Section (__DATA, __const): 1864
Section (__DATA, __cfstring): 4000
Section (__DATA, __objc_classlist): 200
Section (__DATA, __objc_protolist): 8
Section (__DATA, __objc_imageinfo): 8
Section (__DATA, __objc_const): 21760
Section (__DATA, __objc_selrefs): 1776
Section (__DATA, __objc_classrefs): 352
Section (__DATA, __objc_superrefs): 24
Section (__DATA, __objc_ivar): 620
Section (__DATA, __objc_data): 2000
Section (__DATA, __data): 96
Section (__DATA, __bss): 40
Section (__DATA, __common): 8
Section (__LD, __compact_unwind): 15968
Section (__LLVM, __bundle): 822914
total 943476
total 943490

分析结论

(1) LinkMap分析的大小不包含打入framework中的bundle信息,建议用LinkMap分析的包大小至少要加上bundle的大小。

(2)经多次安装实验,真实安装包后, APP大小增长是比LinkMap给出的包大小大的。

(3)给外部的包大小信息,暂时跟大家保持一致“使用LinkMap给出的数据”。

iOS包大小计算的更多相关文章

  1. (一一七)基本文件操作 -SDWebImage清除缓存 -文件夹的大小计算

    在iOS的App沙盒中,Documents和Library/Preferences都会被备份到iCloud,因此只适合放置一些记录文件,例如plist.数据库文件.缓存一般放置到Library/Cac ...

  2. [转]SOCKET通信中TCP、UDP数据包大小的确定

    TCP.UDP数据包大小的确定 UDP和TCP协议利用端口号实现多项应用同时发送和接收数据.数据通过源端口发送出去,通过目标端口接收.有的网络应用只能使用预留或注册的静态端口:而另外一些网络应用则可以 ...

  3. SQLSERVER复制优化之一《改变包大小》

    SQLSERVER复制优化之一<改变包大小> 自从搭了复制之后以为可以安枕无忧了,谁不知问题接踵而来 这次遇到的问题是丢包,不知道情况的读者可以先看一下我之前写的一篇<SQLSERV ...

  4. C++类所占内存大小计算

    C++类所占内存大小计算 说明:笔者的操作系统是32位的. class A {}; sizeof( A ) = ? sizeof( A ) = 1明明是空类,为什么编译器说它是1呢? 空类同样可以实例 ...

  5. apk瘦身(包大小优化)

    最近太忙好久没有写随笔,放假才有空写写随笔. 最近在项目中一直在做包大小优化 随着业务需求的增加,包大小是忍不住的往上涨 为了提高应用市场下载转化率,一直在优化包大小 首先 分析你的apk中占比的主要 ...

  6. [转]TCP、UDP数据包大小的确定

       TCP.UDP数据包大小的确定   http://blog.163.com/jianlizhao%40126/blog/static/1732511632013410101827640/   U ...

  7. TCP、UDP数据包大小的限制(UDP数据包一次发送多大为好)——数据帧的物理特性决定的,每层都有一个自己的数据头,层层递减

    1.概述 首先要看TCP/IP协议,涉及到四层:链路层,网络层,传输层,应用层. 其中以太网(Ethernet)的数据帧在链路层 IP包在网络层 TCP或UDP包在传输层 TCP或UDP中的数据(Da ...

  8. SQLSERVER复制优化之一《减少包大小》

    原文:SQLSERVER复制优化之一<减少包大小> SQLSERVER复制优化之一<减少包大小> 自从搭了复制之后以为可以安枕无忧了,谁不知问题接踵而来 这次遇到的问题是丢包, ...

  9. UDP传输包大小(转)

    源:UDP传输包大小 在进行UDP编程的时候,我们最容易想到的问题就是,一次发送多少bytes好? 当然,这个没有唯一答案,相对于不同的系统,不同的要求,其得到的答案是不一样的,我这里仅对 像ICQ一 ...

  10. mysql查询进程、导入数据包大小设置

    mysql查询进程.导入数据包大小设置 zoerywzhou@163.com http://www.cnblogs.com/swje/ 作者:Zhouwan 2017-12-27 查询正在执行的进程: ...

随机推荐

  1. 基于Vue项目+django写一个登录的页面

    基于Vue项目+django写一个登录的页面 前端 借用了一下vue项目模板的AboutView.vue 页面组件 <template> <div class="about ...

  2. VSCode+EIDE开发CH32V系列RISC-V MCU

    VSCode+EIDE开发CH32V系列RISC-V MCU 1. VS Code Visual Studion Code (VS Code),是一款由微软开发且跨平台的免费源代码编辑器.该软件支持语 ...

  3. TIDB-DM数据迁移第一部(安装部署)

    官方连接: https://docs.pingcap.com/zh/tidb/stable/dm-overview 架构: 1.安装DM download https://tiup-mirrors.p ...

  4. Linux上的I2C基础知识

    Linux上的I2C基础知识 什么是I2C I2C(Inter-Integrated Circuit,eye-squared-C),也称为 I2C 或 IIC,是一种同步.多控制器/多目标(主/从). ...

  5. 提交from表单,method与浏览器请求显示不一致

    <from method="post" action ="/login_check"> 用户名:<input type="text& ...

  6. centos7的密码安全策略加固

    centos7操作系统 在CentOS 7上实现密码复杂度策略设置 一.使用login.defs文件 解析:/etc/login.defs 是设置新建用户帐号限制的文件.该文件里的配置对root用户无 ...

  7. Windows下使用Fortran读取HDF5文件

    需要用Fortran读取HDF5格式的GPM IMERG卫星降水文件,在已经安装HDF5库(参见VS2019+ Intel Fortran (oneAPI)+HDF5库的安装+测试 - chinago ...

  8. linux下第三方库的配置和链接——以opencv为例

    安装OpenCV(可参考链接) 下载source到 /usr/local/路径下 新建 /build/ cmake 编译 添加库路径 vim /etc/ld.so.conf 该目录作用参考链接 输入: ...

  9. go new和make区别

    new用于任何类型,返回的是指针,多用在对象,结构体 make类型只用于slice,map,channel,返回引用,为什么只这三个用make,因为它们都有要初始化的数据,参数golang 内置函数n ...

  10. javascript【基础】数据类型

    五种基本数据类型 Number String Boolean Undefined 一个没有设置值的变量 Null 表示一个空对象引用 ES6(Symbol) //ES6 一种复杂数据类型 Object ...