介绍
分享一款用于剖析iOS
ipa包的脚本东西,运用此东西能够主动扫描发现
可修正的包体积问题,一起能够生成包体积数据用于查看。这块东西咱们团队内部现已运用很长一段时刻,希望能够协助到更多的开发同学更加效率的优化包体积问题。
东西下载地址
背景
APPAnalyze
东西最早诞生主要是为了处理以下包体积管理的问题:
关于定位下沉市场的APP
来讲,包体积是一个非常重要的功能目标,包体积过大会影响用户下载APP
的志愿。可是在早期咱们缺少一些手段协助咱们更高效的去进行包体积管理。
主动发现问题
-
提升效率
– 人工排查问题效率低,关于常见的问题尽或许主动扫描出来。而且关于组件化
工程来讲,许多外部组件是经过Framework
办法供给,没有仓库源码权限用于剖析包体积问题。 -
流程化
– 形成主动化的质量流程,增加到CI流水线
主动发现包体积问题。
数据目标量化
-
包体积问题
– 供给数据化平台查看每个组件的包体积待修正
问题 -
包体积巨细
– 供给数据化平台查看每个组件的包体积占比,包含总巨细
,单个文件二进制巨细
和每个资源巨细
。能够针对不同的APP
版别进行组件化粒度的包体积数据对比,更方便查看每个版别的组件巨细增量。
完结办法
咱们选择了不依靠源码而是直接扫描二进制库的办法来完结这个才能,整体的履行流程一下:
提示:根据组件化工程的扫描办法内部支撑,仅仅暂时不对外开放。
运用指南
装置
无需装置。经过下载链接直接下载终端可履行命令文件APPAnalyzeCommand
到本地即可运用。
APPAnalyzeCommand 下载地址
运用
$ APPAnalyzeCommand --help
OPTIONS:
--version <version> 当前版别 1.0.0
--output <output> 输出文件目录。必传参数
--config <config> 装备JSON文件地址。非必传参数
--ipa <ipa> ipa.app文件地址。必传参数
-h, --help Show help information.
履行
翻开终端程序直接履行以下shell
指令,即可生成ipa
的包体积数据以及包体积待修正问题。
提示:不能直接运用
AppStore
的包,AppStore
的包需求砸壳。主张尽量运用XCodeDebug
的包。
APPAnalyzeCommand --ipa ipas/JDAPP/JDAPP.app --output ipas/JDAPP
提示:假如提示
permission denied
没有权限,履行sudo chmod -R 777 /Users/a/Desktop/ipas/APPAnalyzeCommand
即可。双击APPAnalyzeCommand
是否能够直接引发终端程序。
生成产品
指令履行完结今后,会在ouput
参数指定的文件夹生成APPAnalyze
文件夹。具体文件介绍如下:
包体积信息
-
app_size.html
– 展现ipa
每个framework
的包体积数据,可直接用浏览器翻开。
提示:依照主程序和动态库进行粒度区分
-
framework_size.html
– 展现单个framework
一切的包体积数据,二级页面不要直接翻开
。
提示:
XCode
生成Assets.car
时会将一些小图片拼接成一张PackedAssetImage
的大图片。
-
package_size.json
–ipa
包体积 JSON 数据
包体积待修正问题
-
app_issues.html
– 展现ipa
每个framework
的包体积待修正问题数量,可直接用浏览器翻开。
提示:依照主程序和动态库进行粒度区分
-
framework_issues.html
– 展现单个framework
一切的待修正问题详细数据,二级页面不要独自翻开
。
-
issues.json
–ipa
待修正包体积问题 JSON 数据
提示:
json
数据可用于搭建自己的数据平台,扩展更多的才能。例如查看不同APP版别以及支撑多个APP版别对比等。
规矩介绍
包体积
未运用的类
界说了类没有被运用到,包含ObjC
类和Swift
类。
扫描规矩
- 没有查到到对应的
ObjC
类被引用 - 没有被作为父类运用
- 没有运用的字符串和类名共同
- 没有被作为特点类型运用
- 没有被创立或调用办法
- 没有完结
+load
办法
可选的修正办法
- 移除未运用的类
-
Swift
类假如仅仅用了static
办法考虑修改成Enum
类型 - 假如仅仅在类型转换时运用了也会检测出是未运用的类,例如
(ABCClass *)object;
。主张查看是否真的有没有到相关类后删去 - 关于
ObjC
,假如仅仅作为办法参数类型运用也会被检测出是未运用的类。主张删去相关办法即可。
提示:删去类相对是一种安全的行为,因为删去后假如有被运用到会产生编译时过错。虽然有做字符串调用的扫描过滤,不过仍是主张查看是否或许被
Runtime
动态创立调用
未运用的ObjC协议
界说了ObjC
协议没有被类运用
扫描规矩
- 对应的协议没有被类引用
可选的修正办法
- 移除未运用的协议
Bundle内多Scale图片
Bundle
内同一张图片包含多个Scale
会导致更大的包体积。
扫描规矩
- 同一个
Bundle
内存在同名可是scale
不同的图片。例如a@2x.png
/a@3x.png
可选的修正办法
- 移除
Scale
更低的图片
大资源
文件巨细超越必定巨细的即为大资源,默许为20KB
。
扫描规矩
- 某个文件超越设置的大资源限额
可选的修正办法
- 移除资源动态下发
- 运用更小的数据格局,例如运用更小的图片格局
重复的资源文件
存在多个相同的重复文件。
扫描规矩
- 多个文件
MD5
共同即断定为重复文件。
可选的修正办法
- 移除剩余的文件
未运用的类Property特点
ObjC
类中界说的特点没有被运用到。
扫描规矩
- 对应的特点没有被调用 set/get 办法,一起也没有被
_
的办法运用 - 不是来自完结协议的特点
- 不是来自
Category
的特点 - 不存在字符串运用和特点名共同
可选的修正办法
- 移除对应的特点
- 假如是接口协议的特点,需求增加类完结此接口
注意事项
- 或许存在部分动态运用的场景,需求进行必定的查看。例如一些承继
NSObject
的数据模型类,或许存在特点没有被直接运用到,可是或许会被传唤成JSON
作为参数的状况。例如后台下发的数据模型
未运用的ImageSet/DataSet
包含的Imageset
/DataSet
并没有被运用到。
扫描规矩
- 未检测到和
Imageset
相同姓名的字符串运用
可选的修正办法
- 移除ImageSet/DataSet
注意事项
- 某些
Swift
代码中运用的字符串不能被发现所以会被作为未运用。 - 运用字符串拼接的姓名作为imageset的姓名。
- 被合成到
PackedAssetImage
里的Imageset
不能被扫描出来
未运用的ObjC办法
界说的ObjC
Category 办法并未被运用到。
扫描规矩
- 不存在和此办法相同的办法名运用
- 不存在运用的
字符串
和办法名共同 - 不是来自父类或
Category
的办法 - 不是来自完结接口的办法
- 不是特点 set/get 办法
可选的修正办法
- 移除对应办法
未运用的分类办法
界说的ObjC
Category 办法并未被运用到。
扫描规矩
- 不存在和此办法相同的办法名运用
- 不存在和办法名共同的
字符串
运用 - 不是来自父类或
Category
的办法 - 不是来自完结接口的办法
可选的修正办法
- 移除未运用的办法
- 假如是接口协议的办法,需求增加类完结此接口
未运用的资源文件
包含的文件资源并没有被运用到。这里的资源不包含Imageset
/DataSet
。
扫描规矩
- 未检测到和文件名相同姓名的字符串运用
可选的修正办法
- 移除资源
注意事项
- 某些
Swift
代码中运用的字符串不能被发现所以会被作为未运用 - 运用字符串拼接的姓名作为资源的姓名
安全
动态反射调用ObjC类
存在类名和字符串共同,或许运用NSClassFromString()
办法动态调用类。当字符串
或类名改变时无法运用编译时查看发现问题,或许会导致功用反常。
扫描规矩
- 存在运用的
字符串
和NSObject子类
类名相同
可选的修正办法
- 运用
NSStringFromClass()
获取类姓名符串 - 运用
Framework
外部的类应该运用办法封装,除了少部分功用不应该运用反射去调用类
提示:包含承继
NSObject
的 swift 类。
ObjC特点内存声明过错
一些特别的NSObject
类型的特点内存类型声明过错,或许会导致功用反常或触发Crash
。
扫描规矩
-
NSArray
/NSSet
/NSDictionary
类型的特点运用strong
声明 -
NSMutableArray
/NSMutableSet
/NSMutableDictionary
类型的特点运用copy
声明
可选的修正办法
- 修改
strong
/copy
声明
冲突的分类办法
ObjC
同一个类的多个Category
分类中存在多个相同的办法,因为运行时终究会加载办法或许是不确定的,或许会导致功用反常等不知道的行为。
扫描规矩
-
NSObject类
的多个Category
分类中存在多个相同的办法
修正办法
- 移除剩余的分类办法
重复的分类办法
ObjC
原始类和类的Category
分类中有相同的办法,分类中的办法会掩盖原始类的办法,或许会导致功用反常等不知道的行为。
扫描规矩
-
NSObject
原始类和类的Category
分类中有相同的办法
修正办法
- 移除重复的分类办法
未完结的ObjC协议办法
类完结了某个ObjC
协议,可是没有完结协议的非可选
办法。或许会导致功用反常或触发Crash
。
扫描规矩
-
类
和分类
未完结NSObject
协议的非可选
办法
可选的修正办法
- 对应的类完结缺失的
非可选
协议办法 - 将对应的协议办法标识为
optional
可选办法
重复的ObjC类
多个动态库
和静态库
之间存在相同的类
。不会导致编译失利,可是运行时只会运用其中一个类,或许会导致功用反常或触发Crash
。一起会增加包体积
。
扫描规矩
- 多个
动态库
和静态库
之间存在相同的NSObject类
符号
或许的修正办法
- 移除重复的类
功能
运用动态库
运用动态库会增加发动
耗时。
扫描规矩
-
Macho
为动态库
可选的修正办法
- 运用
静态库
- 运用
Mergeable Library
完结+load
办法的类
APP发动
后会履行一切+load
办法,减少+load
办法能够下降发动耗时。
扫描规矩
- 完结
+load
办法的NSObject
类
可选的修正办法
- 移除
+load
办法 - 运用
+initialize
替代
自界说装备
重要装备
systemFrameworkPaths
能够根据本身项目进行体系库目录的装备,解析工程时也会对体系库进行解析。装备体系库目录关于未运用办法的查找能够供给更多的信息避免误报。可是装备更多会导致履行的更慢,主张至少装备Foundation
/UIKit
。
unusedObjCProperty-enable
unusedObjCProperty
规矩默许不敞开。
- 敞开未运用特点查看今后,会扫描
macho
的__TEXT
段,会增加剖析的耗时。
unusedClass-swiftEnable
unusedClass-swiftEnable
默许不敞开。
- 敞开
Swift
类查看今后,会扫描macho
的__TEXT
段,会增加剖析的耗时。 - 未运用
Swift
类的项目主张不要敞开,假如考虑履行功能的话Swift
运用相对比较多的再敞开。
提示:扫描
macho
的__TEXT
段需求运用XCode
Run编译出的包,不能直接运用用于上架APP Store
构建出的包。主要是Debug
会包含更多的信息用于扫描。
装备特点
APPAnalyzeCommand -ipa /Users/Desktop/ipas/APPMobile/APPMobile.app -config /Users/Desktop/ipas/config.json --output /Users/Desktop/ipas/APPMobile
可根据本身项目需求,增加下列规矩可装备参数。在运用APPAnalyzeCommand
指令时增加--config
装备文件地址。
{
"systemFrameworkPaths": ["/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/UIKitCore.framework/UIKitCore", "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation",
"/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/Foundation.framework/Foundation"
], // 装备体系库。会极大增加未运用办法的误报
"rules": {
"dynamicCallObjCClass": { // 动态调`ObjC类
"enable": false, // 是否启用
"excludeClasslist": [ // 过滤类名
"NSObject",
"param"
]
},
"incorrectObjCPropertyDefine": { // 过错的 ObjC 特点界说
"enable": false // 是否发动
},
"largeResource": { // 大资源
"maxSize": 20480 // 装备大资源断定巨细。默许 20480Byte=20KB
},
"unusedObjCProperty": { // 未运用的 ObjC 特点
"enable": false, // 是否启用。默许不敞开
"excludeTypes": ["NSString", "NSArray", "NSDictionary", "NSNumber", "NSMutableArray", "NSMutableDictionary", "NSSet"] // 过滤掉部分类型的特点
},
"unusedClass": { // 未运用的类
"swiftEnable": false, // 是否支撑 Swift 类。默许不支撑
"excludeSuperClasslist": ["JDProtocolHandler", "JDProtocolScheme"],// 假如类承继了某些类就过滤
"excludeProtocols": ["RCTBridgeModule"], // 假如类完结了某些协议就过滤
"excludeClassRegex": ["^jd.*Module$", "^PodsDummy_", "^pg.*Module$", "^SF.*Module$"] // 过滤掉姓名契合正则表达式的类
},
"unusedObjCMethod": { // 未运用的 ObjC 办法
"excludeInstanceMethods": [""], // 过滤掉某些姓名的对象办法
"excludeClassMethods": [""], // 过滤掉某些姓名的类办法
"excludeInstanceMethodRegex": ["^jumpHandle_"], // 过滤掉姓名契合正则表达式的对象办法
"excludeClassMethodRegex": ["^routerHandle_"], // 过滤掉姓名契合正则表达式的类办法
"excludeProtocols": ["RCTBridgeModule"] // 假如类集成了某些协议就不再查看,例如 RN 办法
},
"loadObjCClass": { // 调用 ObjC + load 办法
"excludeSuperClasslist": ["ProtocolHandler"], // 假如类承继了某些类就过滤
"excludeProtocols": ["RCTBridgeModule"] // 假如类完结了某些协议就过滤,例如 RN 办法
},
"unusedImageset": { // 未运用 imageset
"excludeNameRegex": [""] // 过滤掉姓名契合正则表达式的imageset
},
"unusedResource": { // 未运用资源
"excludeNameRegex": [""] // 过滤掉姓名契合正则表达式的资源
}
}
}
其他
扫描质量怎么
这套东西咱们团队内部开发加逐步完善有一年的时刻了。根据此东西修改了几十个组件的包体积问题,一起不断的修正误报问题。现在现有供给的这些规矩查看误报率是很低的,只要极少数几个规矩或许存在误报的或许性,整体扫描质量仍是很高的。
和社区开源的东西有什么差异
咱们在早期调研了社区的几个同类型的开源东西,主要存在以下几个问题:
-
扩展性不行
– 无法支撑项目更好的扩展定制才能,例如增加扫描规矩。 -
功用不全
– 只供给部分才能,例如只供给未运用资源
或许未运用类
。 -
无法生成包体积数据
– 无法生成包体积完整的数据。 -
查看质量不高
– 扫描发现的过错数据多,或许有一些问题不能被发现。
开源方案
后续必定会开源。最近刚做完必定的代码重构,然后准备申请公司的开源流程。
开源带来的优点
开源带来的优点是,部分工程能够根据本身的业务需求,扩展定制自己的扫描东西。一起也能够将一些更好的主意完结增加进来。
-
扩展解析办法
– 现在只支撑ipa
模式扫描,很快会开放支撑project
组件化工程的扫描办法。根据组件化工程
的扫描能够更加准确,可是不同的公司组件化工程
的构建办法或许是不相同的,有需求能够在上层定制本身组件化工程
的扫描解析。 -
扩展扫描规矩
– 虽然现在现已增加了比较多的通用性的规矩,一起供给了必定的灵活性装备才能。可是不同的项目或许需求定制一些其他的规矩,这些规矩没办法经过在现有规矩上增加装备才能完结。 -
扩展数据生成
– 默许包里只包含两种数据生成,包体积
数据还有包体积待修正问题
数据。能够扩展更多的数据生成格局,例如咱们本身的项目就有增加根据组件的依靠树格局。
后续规划
组件化工程扫描
近期方案优先支撑根据组件化工程的扫描办法。方案供给一种通用的才能,能够经过装备包含一切组件信息(二进制/资源/依靠联系)的这样一个json
文件来装备组件工程。 不过开源后也能够根据本身的需求来定制解析办法。
根据组件化扫描办法有以下优势:
-
细化数据粒度
– 能够细化每个模块的包体积和包体积问题,更容易进行包体积优化。 -
更多的查看
– 例如查看不同组件同一个Bundle
包含同名的文件,不同组件包含同一个category
办法的的完结。 -
查看结果更准确
– 例如ObjC
未运用办法的查看,只要存在一个和办法名相同的调用就表明办法有被运用到。可是整个ipa
中或许存在许多相同的办法名可是只要一个办法有真正被调用到,假如细分到组件的粒度就能够发现更多问题。
提示:只要APP主工程无代码,全部经过子组件以
framework
的形式导入二进制库的办法的工程才合适这种模式。
关于 Swift 更好的支撑
关于Swift
言语只要敞开XCode
编译优化今后就能在生成产品的时候支撑无用代码的移除,包含未运用类型
和未运用办法
的主动移除,可是仍然有部分场景不会进行优化。所以这一块也是后续完善的重点:
-
未运用类
– 编译器不会关于未运用class
进行移除,即使是承继NSObject
的子类。 -
未运用特点
– 编译器不会关于未运用特点
进行移除,包含class
和struct
的特点。 -
未运用办法
– 关于class
的办法,编译器并不会进行移除,即使没有声明@objc
进行音讯派发。
相关链接
- Github地址