iOS 进阶 (三) -- 如何给App瘦身

一、前言

大于150M的App只能通过WiFi来下载的,假如正巧有用户想使用你们的App,但没有WiFi,又看了看其他竞品,而他们的App正好小于150M,那这个用户你铁定流失掉了,所以在提升新用户这方面,App瘦身也起到了一定的作用。下面就谈一谈可以用到的瘦身方法。

二、App Thinning

1.介绍

App Thinning是苹果推出的一项可以改善App下载进程的新技术,主要就是为了解决用户下载App耗费过高流量的问题,同时还可以节省用户iOS设备的存储控件。App Thinning会专门针对同的设备来选择只适用于当前设备的内容以供下载。比如,2x和3x,类似plus的机型只会下载3x分辨率的图片,而其它机型只下载2x。
我们所知道的芯片指令集架构文件,有如下几种:

模拟器:x86_64i386
真机:arm64armv7armv7s

App Thinning会在下载时选择最适合自己的设备的芯片指令集架构文件。
App Thining有三种方式:App SlicingBitcodeOn-Demand Resources

  • App Slicing:在上传iTunes Connect后,对App做切割,创建不同的变体,这样就可以使用到不同的设备
  • On-Demand Resources:主要是为游戏多关卡场景服务。它会根据关卡进度下载随后几个关卡资源,并且已经过关的资源会被删掉,这样就可以减少出装App的包大小。
  • Bitcode:针对特定设备进行包大小优化,优化不明显

2.使用

在创建工程后会看到Assets.xcassets文件夹,这里就是放所有图片资源的地方,我们只需要将2x和3x图放入进去,剩下的xcode会自动帮我们完成。

三、定时清理无用的图片资源和类文件

随着新需求开发的迭代速度越来越快,很多图片资源和类文件也会越来越多,如果不定时清理,一来会增加包的大小,二来在开发时众多的类也让人看起来眼花缭乱,在这里给大家介绍两个开源的Mac应用程序,LSUnusedResourcesXcodeProjectArrangementTool,对于LSUnusedResources来说,灵活性更高些,可以自定义添加正则来筛选,不过在使用这两种方法时,需要进行二次确认。
而它们主要的原理可以分为如下6个步骤:

  • 通过 find 命令获取 App 安装包中的所有资源文件,比如find xxx/Project/ -name
  • 设置用到的资源的类型,如jpg、png、gif、webP
  • 使用正则匹配在源码中找出使用到的资源名,比如 pattern = @"@"(.+?)""
  • 使用find命令找到的所有资源文件,再去掉代码中使用到的资源文件,剩下的就是无用的资源了
  • 对于按照规则设置的资源名,我们需要在匹配使用资源的正则表达式里添加相应的规则,比如image_%d
  • 确认无用资源后,对这些无用的资源进行删除操作。

四、图片压缩

一个华丽丽的App里成有着数不尽的资源图片,那对App进行瘦身操作,这些图片的处理也就成为了一个重点的操作对象。而对它们最好处理,就是在不损失图片质量的前提下尽可能地压缩处理。目前比较好的压缩方案是,将图片转成WebP。WebP是Google的一个开源项目,使用它的好处是:

  • WebP压缩率高,而且肉眼看不出差异,同时支持有损和无损两种压缩模式。比如将Gif图转为Animated WebP,有损压缩模式下可减少64%大小,无损压缩模式下可减少19%大小。
  • WebP支持Alpha透明和24-bit颜色数,不会像PNG8好样因为色彩不够而出现毛边。

那如何将图片转为WebP呢?

1.cwebp

Google在开源WebP的同时,还提供了一个图片压缩工具,cwebp,使用语法如下:

cwebp [options] input_file -o output_file.webp

比如,无损压缩模式

cwebp -lossless a.png -o b.webp

-lossless表示进行无损编码,不使用-lossless则表示有损压缩
-q (float)参数,可以在不损失图片质量的情况下进行最大化的压缩

  • 小于256色适合无损压缩,压缩率高。-lossless -q 100
  • 大于256色使用75%有损压缩。-q 75
  • 远大于256色使用75%以下压缩率。-q 50 -m 6

2.iSparta

iSparta是由腾讯开发的GUI工具,操作方便快捷,除了实现将PNG转WebP外,还提供批量处理和记录操作配置的功能。

如果是其他格式的图片要转成WebP的话,需要先将其转成PNG格式,再转成WebP格式

要使用WebP格式的图片,我们需要在显示图片时使用libwebp进行解析,libwebp Demo
WebP在CPU消耗和解码时间会比PNG高两倍,所以在使用时,需要在性能和包瘦身上做取舍。

建议是,图片大小超过100KB,可以考虑使用WebP,小于100KB,可以使用网页工具TinyPng或者GUI工具ImageOptim进行图片压缩。这两个工具压缩率没有WebP高,不会改变图片压缩方式,所以解析时对性能损耗也不会增加。

3.矢量图PDF

在Xcode6以上就支持在Asset Catalog上使用PDF矢量图来代替原来不同的分辨率图片。只需要将矢量图导入Asset Catalog中,然后在Show Attributes inspector设置Scale FactorSingle Vector即可。这样就不需要考虑2x与3x,工程里也就只有一套资源图,而剩下的工作还是全都交给Xcode即可。

五、代码瘦身

  • 首先,找出方法和类的全集
  • 然后,找到使用过的方法和类
  • 接下来,取两者的差集得到无用的代码
  • 最后,由人工确认无用代码可删除后,进行删除

1.LinkMap结合Mach-O找无用代码

打开LinkMap设置

  • TARGETS -> Build Settings -> Write Link Map File 设置为YES
  • 设置LinkMap文件路径,添加Path to Link Map File输出路径

LinkMap文件分三部分:Object FileSectionSymbols

  • Objct File包含了代码工程的所有文件
  • Section描述了代码段在生成的Mach-O里的便宜位置和大小
  • Symbols会列出每个方法、类、block以及它们的大小

通过LinkMap,不光可以统计出所有的方法和类,还能够清晰地看到代码所占包大小的具体分布,进而有针对性地进行代码优化。
得到了代码的全集信息以后,我们还需要找到已使用的方法和类,这样才能获取到差集,找出无用代码。下面就说说怎样通过Mach-O取到使用过的方法和类。
iOS的方法都会通过objc_msgSend来调用,而objc_msgSendMach-O文件里是通过__objc_selrefs这个section来获取selector这个参数的。所以__objc_selrefs里的方法一定是被调用了的。__objc_classrefs里是被调用过的类,__objc_superrefs是调用过supper的类。通过__objc_classrefs__objc_superrefs,我们就可以找出使用过的类和子类。
然后可以使用MachOView来查看Mach-O文件里的信息。

将编译后生成的xxx.app包揭开,取出对应的工程文件同名的文件,然后使用MachOView打开,就可以看到里面的信息了

但这种方法并不是完美的,还会有些问题。原因就在于,Objective-C是一门动态语言,方法调用可以写成在运行时动态调用,这样就无法收集全所有调用的方法和类。所以通过这种方法找出的无用方法和类就只能作为参考,还需要二次确认。

2.通过AppCode找出无用代码

AppCode适用于工程量不是很大的项目,使用起来也很简单,可以直接在AppCode里选择Code->Inspect Code就可以进行静态分析,分析完以一,可以在Unused code里看到所有的无用代码

image.png

  • 无用类:Unused class是无用类,Unused import statement是无用类引入声明,Unused property是无用类的属性
  • 无用方法:Unused method是无用的方法,Unused parameter是无用参数,Unused instance variable是无用的实例变量,Unused local variable是无用的局部变量,Unused value是无用的值
  • 无用宏:Unused macro是无用的宏
  • 无用全局:Unused global declaration是无用全局声明

AppCode静态检查的问题:

  • JSONModel里定义了未使用的协议会被判定为无用协议
  • 如果子类使用了父类的方法,父类的这个方法不会被认为使用了
  • 通过点的方式使用属性,该属性会被认为没有使用
  • 使用performSelector方式调用的犯法也检查不出来
  • 运行时声明类的情况检查不出来。比如NSClassFromString方式调用的类会被查出为没有使用的类,像这样不指定类名的方式使用的类,会被认为该类没有被使用。

基于以上种种原因,使用AppCode检查出来的无用代码,也需要人工二次确认后,才能够安全删除掉。

3.运行时检查类是否真正被使用过

在App不断迭代过程中,新人不断接受、为务功能需求不断替换,会留下很多无用代码。这些代码在执行静态检查时会被用到,但是使用它的入口可能已经没有了,所以这部份代码也是可以被删掉的。
通过runtime源码,我们可以找到如何判断一个类是否初始化过的函数

#define RW_INITIALIZED (1<<29)
bool isInitialized() {
   return getMeta()->data()->flags & RW_INITIALIZED;
}

isInitialized的结果会保存到猿类class_rw_t结构体的flags信息里,flags的1<<29位记录的就是这个类是否初始化了的信息。而flags的其它位记录的信息,可以参看源码

// 类的方法列表已修复
#define RW_METHODIZED         (1<<30)

// 类已经初始化了
#define RW_INITIALIZED        (1<<29)

// 类在初始化过程中
#define RW_INITIALIZING       (1<<28)

// class_rw_t->ro 是 class_ro_t 的堆副本
#define RW_COPIED_RO          (1<<27)

// 类分配了内存,但没有注册
#define RW_CONSTRUCTING       (1<<26)

// 类分配了内存也注册了
#define RW_CONSTRUCTED        (1<<25)

// GC:class 有不安全的 finalize 方法
#define RW_FINALIZE_ON_MAIN_THREAD (1<<24)

// 类的 +load 被调用了
#define RW_LOADED             (1<<23)

既然能过在运行中看到类是否初始化了,那么我们就能够找出有哪些类是没有初始化的,即找到在真实环境中没有用到的类,进行二次确认后,再清理掉。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念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

推荐阅读更多精彩内容