iOS-APP瘦身

屏幕快照 2016-07-15 上午10.47.09.png

资源优化

1. 去除无用资源

一般都是版本迭代过程中存在的图片资源。
可以借助三方工具来解决: Unused-master LSUnusedResources

通过Unused-master可以查找到一些工程中没有用到的图片,但这个工具并不是百分百正确,在图片删除的时候要慎重看一下。在扫描结果中,查找到了一些命名错误的图片,例如:xxx2x.png、xxx.png这样的图片命名方式会影响APP性能

D3BEE3F9-029C-41E3-B704-772A3EA0EB0A.png
2. 资源压缩
2.1. 图片无损压缩

这个主要是利用三方工具ImageOptim来实现:

屏幕快照 2016-07-04 下午1.54.17.png

ImageOptim Mac版是一款非常简单的图片大小优化工具。只要拖动图片到软件界面就可以自动把图片的大小进行优化。ImageOptim Mac版对于开发人员和设计人员一定还有用处,如文件的EXIF标签和颜色配置文件等,达到优化减小占用磁盘空间。

图片文件中往往包含一些注释、颜色 Profile 等多余信息,移除后图像质量不变,体积更小载入更快。ImageOptim 以此方式压缩图片,先分析图片,找到最优压缩参数,去除无关信息减小体积,实行无损压缩。

62B4D991-1827-491C-9FC9-C9C94C6C5567.png

关于图片资源补充:

1.能用缩略图的用缩略图、单一色彩可以用纯色代替
2.尽量使用8-bit图片使用8-bit的PNG图片,比32-bit的图片能减少4倍的压缩率。 由于8-bit的图片支持最多256种不同的颜色,所以8-bit的图片一般只应该用于一小 部分的颜色图片。例如灰度图片最好使用8-bit。

2.2. 音频压缩

参考WWDC中的Audio Development for Games,里面介绍了如何有效的处理音频。常规来说,我们要使用AAC或MP3来压缩音频,并且可以尝试降低一下音频的比特率。有时候44.1khz的采样是没有必要的,稍微低一点的比特率也不会降低音频的质量。

2.3. 查找文件大小

通过查找工程中文件的大小,可以找到一些较大的图片和三方库,视情况压缩或替换这个主要是用命令行,cd到工程目录 然后输入find . -size +100 就会遍历出工程目录下大于100k的文件,如果是+1000 就是大于1000k的,以此类推...
例如:Robin:SimpleFinance zhaojijin$ find . -size +1000

0F7BB855-3C4F-424E-B1C5-788B53FC107A.png

这个方法扫描到的文件并不是完全正确,如上图,这个结果中changeCard_lostCardStepOnePic@3x.png finder中实际大小是854k,但这并不影响使用,大体还是对的

3.不常用资源换为下载

比如自定义字体,可以在APP第一次启动后动态获取

编译优化

1. 去除符号信息

Strip Debug Symbols During Copy 和 Symbols Hidden by Default 在release版本应该设为yes,可以去除不必要的调试符号,当然xcode默认就是yes

2. 开启编译优化

Build Settings->Optimization Level有几个编译优化选项,release版应该选择Fastest, Smalllest,这个选项会开启那些不增加代码大小的全部优化,并让可执行文件尽可能小。

3. 避免编译多个框架(但多个框架在不同机型上运行速度相对快点,iOS9 app thinning)

如果应用需要在多种cpu架构下运行,那么xcode生成的二进制文件会包含对应架构的多个副本,这样可执行文件的大小就会成倍增加。
arm cpu架构可以标识为armvX (64),X代表一个数字,如果不指定是64位,就是指32位。一般来说arm架构都是向后兼容的,armv6的代码可以在armv7上运行,32位的代码可以在64位cpu上运行。(buidsettings->Architectures)

可执行文件优化

在项目里新建一个类,给它添加几个方法,但不要在任何地方import它,build完项目后观察linkmap,你会发现这个类还是被编译进可执行文件了。
linkmap文件是xcode link时产生的中间文件,一般用于调试,可以精确知道某个地址对应的函数。
对此我们可以通过脚本,遍历整个项目的文件,找出所有没有被引用的类文件和没有被调用的方法,在保证没有其他地方动态调用的情况下把它们去掉。如果整个项目历时很长,历时代码遗留较多,这个清理对可执行文件省出的空间还是挺可观的。

可执行文件的组成

XCode开启编译选项Write Link Map File
XCode -> Project -> Build Settings -> 搜map -> 把Write Link Map File选项设为yes,并指定好linkMap的存储位置

屏幕快照 2016-07-05 下午6.08.47.png

编译后,到编译目录里找到该txt文件,文件名和路径就是上述的Path to Link Map File位于~/Library/Developer/Xcode/DerivedData/XXX-eumsvrzbvgfofvbfsoqokmjprvuh/Build/Intermediates/XXX.build/XXX-iphoneos/XXX.build/XXX-LinkMap-normal-armXX.text
其中XXX-eumsvrzbvgfofvbfsoqokmjprvuh的命名是不确定的这个LinkMap里展示了整个可执行文件的全貌,列出了编译后的每一个.o目标文件的信息(包括静态链接库.a里的),以及每一个目标文件的代码段,数据段存储详情。

1.在LinkMap里首先列出来的是目标文件列表:

[0] linker synthesized
[1] /Users/zhaojijin/Library/Developer/Xcode/DerivedData/SimpleFinance-cvxujvtykofyxphauukoxkqhstcn/Build/Intermediates/SimpleFinance.build/Release-iphoneos/SimpleFinance.build/Objects-normal/arm64/UIImageView+HighlightedWebCache.o
[2] /Users/zhaojijin/Library/Developer/Xcode/DerivedData/SimpleFinance-cvxujvtykofyxphauukoxkqhstcn/Build/Intermediates/SimpleFinance.build/Release-iphoneos/SimpleFinance.build/Objects-normal/arm64/YKHomeWaitToBeMatchTitleItem.o

...

[1217] /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk/usr/lib/libobjc.tbd
[1218] /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk/usr/lib/libSystem.tbd
[1219] /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk/System/Library/Frameworks//Accelerate.framework/Accelerate.tbd

前面中括号里的是这个文件的编号,后面会用到,像项目里引用到静态链接库里的目标文件都会在这里列出来。

2.接着是一个段表,描述各个段在最后编译成的可执行文件中的偏移位置及大小,包括了代码段(__TEXT,保存程序代码段编译后的机器码)和数据段(__DATA,保存变量值)

    # Sections:
    # Address   Size        Segment Section
    0x100006660 0x0049CA0C  __TEXT  __text
    0x1004A306C 0x00002424  __TEXT  __stubs
    0x1004A5490 0x00002418  __TEXT  __stub_helper
    0x1004A78A8 0x00017790  __TEXT  __gcc_except_tab
    0x1004BF038 0x0005A3E7  __TEXT  __objc_methname
    0x100519420 0x00055D9C  __TEXT  __cstring
    0x10056F1BC 0x000082C0  __TEXT  __objc_classname
    0x10057747C 0x0000A9B8  __TEXT  __objc_methtype
    0x100581E40 0x0001BAE8  __TEXT  __const
    0x10059D928 0x0000436E  __TEXT  __ustring
    0x1005A1C98 0x0001235C  __TEXT  __unwind_info
    0x1005B4000 0x000006A8  __DATA  __got
    0x1005B46A8 0x00001818  __DATA  __la_symbol_ptr
    0x1005B5EC0 0x0001B710  __DATA  __const
    0x1005D15D0 0x0003B900  __DATA  __cfstring
    0x10060CED0 0x00002988  __DATA  __objc_classlist
    0x10060F858 0x00000028  __DATA  __objc_nlclslist
    0x10060F880 0x00000320  __DATA  __objc_catlist
    0x10060FBA0 0x00000018  __DATA  __objc_nlcatlist
    0x10060FBB8 0x000003D8  __DATA  __objc_protolist
    0x10060FF90 0x00000008  __DATA  __objc_imageinfo
    0x10060FF98 0x000FFB30  __DATA  __objc_const
    0x10070FAC8 0x000145F0  __DATA  __objc_selrefs
    0x1007240B8 0x00000080  __DATA  __objc_protorefs
    0x100724138 0x00002A28  __DATA  __objc_classrefs
    0x100726B60 0x00001B30  __DATA  __objc_superrefs
    0x100728690 0x00005814  __DATA  __objc_ivar
    0x10072DEA8 0x00019FA0  __DATA  __objc_data
    0x100747E48 0x00002E20  __DATA  __data
    0x10074AC68 0x00002120  __DATA  __bss
    0x10074D000 0x00000800  __DATA  __common

首列是数据在文件的偏移位置,第二列是这一段占用大小,第三列是段类型,代码段和数据段,第四列是段名称。

每一行的数据都紧跟在上一行后面,如第二行__symbol_stub的地址0x00275FD0就是第一行__text的地址0x00002740加上大小0x00273890,整个可执行文件大致数据分布就是这样。

这里可以清楚看到各种类型的数据在最终可执行文件里占的比例,例如__text表示编译后的程序执行语句,__data表示已初始化的全局变量和局部静态变量,__bss表示未初始化的全局变量和局部静态变量,__cstring表示代码里的字符串常量,等等。

3.接着就是按上表顺序,列出具体的按每个文件列出每个对应字段的位置和占用空间

    # Address   Size        File  Name
    0x100006660 0x00000018  [  1] -[UIImageView(HighlightedWebCache) sd_setHighlightedImageWithURL:]
    0x100006678 0x00000014  [  1] -[UIImageView(HighlightedWebCache) sd_setHighlightedImageWithURL:options:]
    0x10000668C 0x00000058  [  1] -[UIImageView(HighlightedWebCache) sd_setHighlightedImageWithURL:completed:]
    0x1000066E4 0x0000005C  [  1] -[UIImageView(HighlightedWebCache) sd_setHighlightedImageWithURL:options:completed:]
    ...

同样首列是数据在文件的偏移地址,第二列是占用大小,第三列是所属文件序号,对应上述Object files列表,最后是名字。

1. 去除无用代码

在项目里新建一个类,给它添加几个方法,但不要在任何地方import它,build完项目后观察linkmap,你会发现这个类还是被编译进可执行文件了。
对此我们可以通过脚本,遍历整个项目的文件,找出所有没有被引用的类文件和没有被调用的方法,在保证没有其他地方动态调用的情况下把它们去掉。如果整个项目历时很长,历时代码遗留较多,这个清理对可执行文件省出的空间还是挺可观的。

2. 统计库占用

项目里会引入很多第三方静态库,如果能知道这些第三方库在可执行文件里占用的大小,就可以评估是否值得去找替代方案去掉这个第三方库。我们可以从linkmap中统计出这个信息,利用三方的node.js脚本,可以通过linkmap统计每个.o目标文件占用的体积和每个.a静态库占用的体积,并进行排序

3. 混淆类/方法名

观察linkmap可以发现每个类和方法名都在__cstring段里都存了相应的字符串值,所以类和方法名的长短也是对可执行文件大小是有影响的,原因还是object-c的动态特性,因为需要通过类/方法名反射找到这个类/方法进行调用,object-c对象模型会把类名,方法名列表都保存下来。
可以考虑在编译前把所有类和方法名进行混淆,把长名字替换成短名字,这样做的好处除了缩小体积外,还对安全性有很大提升,别人拿到可执行文件对它class-dump出来的结果都是混淆后的类和方法名,就无法从类和方法名中猜出某个方法是做什么的,就难以挂钩子进行hack。不过这样有个缺点就是crash堆栈反解出来的堆栈方法名会是混淆后的,需要再加一层混淆->原名的转换,实现和使用成本有点高。

4. 减少冗余字符串

代码上定义的所有静态字符串都会记录在在可执行文件的__cstring段,如果项目里Log非常多,这个空间占用也是可观的,也有几百K的大小,可以考虑清理所有冗余的字符串。另外如果有特别长的字符串,建议抽离保存成静态文件,因为AppStore对可执行文件加密导致压缩率低,特别长的字符串抽离成静态资源文件后压缩率会比在可执行文件里高很多。
替换NSLog为DLog:

image.png

类、方法名、属性名等命名长短影响包大小:

91FF2BF6-D5C3-4F00-89BC-ED4D6A3CD15F.png
5. ARC->MRC降低8%空间占用(可忽略,不实用)
image.png

homebrew

Homebrew官网

ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
参考:

iOS工程瘦身

iOS微信安装包瘦身

iOS可执行文件瘦身方法

iOS 对源代码进行混淆

iOS APP可执行文件的组成

用OCLint给iOS代码做静态分析

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容

  • 文章最后有我的 12 条小总结。 写在前面 最近公司需求不多,正好研究一下 App 瘦身的办法,写了点小总结。 如...
    Damonwong阅读 7,594评论 14 76
  • 缩减iOS安装包大小是很多中大型APP都要做的事,一般首先会对资源文件下手,压缩图片/音频,去除不必要的资源。这些...
    buptwsg阅读 1,942评论 0 8
  • 更多内容请挪步我的博客 图片资源删减 使用工具查找未引用资源 LSUnusedResources 比之前的 Unu...
    AliciaRain阅读 655评论 1 0
  • iOS App瘦身 关于app瘦身,你能想到什么? 删除无用类 删除无用方法 代码相似度分析 删除无用图片 无损压...
    码农二哥阅读 407评论 2 3
  • 《必然》第二章 知化(一) 知化:赋予对象认知能力。 从目前人工智能技术的发展速度来看,现在出生的孩子在成年后可能...
    日子林光阅读 467评论 3 2