来源:京东云开发者
自动发现问题
提升效率 - 人工排查问题效率低,对于常见的问题尽可能自动扫描出来。并且对于组件化工程来讲,很多外部组件是通过Framework方式提供,没有仓库源码权限用于分析包体积问题。 流程化 - 形成自动化的质量流程,添加到CI流水线自动发现包体积问题。
数据指标量化
包体积问题 - 提供数据化平台查看每个组件的包体积待修复问题 包体积大小 - 提供数据化平台查看每个组件的包体积占比,包括总大小,单个文件二进制大小和每个资源大小。可以针对不同的APP版本进行组件化粒度的包体积数据对比,更方便查看每个版本的组件大小增量。

提示:基于组件化工程的扫描方式内部支持,只是暂时不对外开放。
安装
使用
$ /Users/Test/APPAnalyzeCommand --helpOPTIONS: --version当前版本 1.0.0 --output
执行
提示:不能直接使用AppStore的包,AppStore的包需要砸壳。建议尽量使用XCodeDebug的包。
/Users/Test/APPAnalyzeCommand --ipa ipas/JDAPP/JDAPP.app --output ipas/JDAP
提示:如果提示permission denied没有权限,执行sudo chmod -R 777 /Users/a/Desktop/ipas/APPAnalyzeCommand即可。
生成产物

包体积信息
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类被引用 没有被当做父类使用 没有使用的字符串和类名一致 没有被当做属性类型使用 没有被创建或调用方法 没有实现+load方法
可选的修复方式
移除未使用的类 Swift类如果只是用了static方法考虑修改成Enum类型 如果只是在类型转换时使用了也会检测出是未使用的类,例如(ABCClass *)object;。建议检查是否真的有没有到相关类后删除 对于ObjC,如果只是作为方法参数类型使用也会被检测出是未使用的类。建议删除相关方法即可。
提示:删除类相对是一种安全的行为,因为删除后如果有被使用到会产生编译时错误。虽然有做字符串调用的扫描过滤,不过还是建议检查是否可能被Runtime动态创建调用
未使用的ObjC协议
扫描规则
对应的协议没有被类引用
可选的修复方式
移除未使用的协议
Bundle内多Scale图片
扫描规则
同一个Bundle内存在同名但是scale不同的图片。例如a@2x.png/a@3x.png
可选的修复方式
移除Scale更低的图片
大资源
扫描规则
某个文件超过设置的大资源限额
可选的修复方式
移除资源动态下发 使用更小的数据格式,例如使用更小的图片格式
重复的资源文件
扫描规则
多个文件MD5一致即判定为重复文件。
可选的修复方式
移除多余的文件
未使用的类Property属性
扫描规则
对应的属性没有被调用 set/get 方法,同时也没有被_的方式使用 不是来自实现协议的属性 不是来自Category的属性 不存在字符串使用和属性名一致
可选的修复方式
移除对应的属性 如果是接口协议的属性,需要添加类实现此接口
注意事项
可能存在部分动态使用的场景,需要进行一定的检查。例如一些继承NSObject的数据模型类,可能存在属性没有被直接使用到,但是可能会被传唤成JSON作为参数的情况。例如后台下发的数据模型
未使用的ImageSet/DataSet
扫描规则
未检测到和Imageset同样名字的字符串使用
可选的修复方式
移除ImageSet/DataSet
注意事项
某些Swift代码中使用的字符串不能被发现所以会被当做未使用。 使用字符串拼接的名字作为imageset的名字。 被合成到PackedAssetImage里的Imageset不能被扫描出来
未使用的ObjC方法
扫描规则
不存在和此方法一样的方法名使用 不存在使用的字符串和方法名一致 不是来自父类或Category的方法 不是来自实现接口的方法 不是属性 set/get 方法
可选的修复方式
移除对应方法
未使用的分类方法
扫描规则
不存在和此方法一样的方法名使用 不存在和方法名一致的字符串使用 不是来自父类或Category的方法 不是来自实现接口的方法
可选的修复方式
移除未使用的方法 如果是接口协议的方法,需要添加类实现此接口
未使用的资源文件
扫描规则
未检测到和文件名同样名字的字符串使用
可选的修复方式
移除资源
注意事项
某些Swift代码中使用的字符串不能被发现所以会被当做未使用 使用字符串拼接的名字作为资源的名字
安全
动态反射调用ObjC类
扫描规则
存在使用的字符串和NSObject子类类名相同
可选的修复方式
使用NSStringFromClass()获取类名字符串 使用Framework外部的类应该使用方法封装,除了少部分功能不应该使用反射去调用类
提示:包含继承NSObject的 swift 类。
ObjC属性内存申明错误
扫描规则
NSArray/NSSet/NSDictionary类型的属性使用strong申明 NSMutableArray/NSMutableSet/NSMutableDictionary类型的属性使用copy申明
可选的修复方式
修改strong/copy申明
冲突的分类方法
扫描规则
NSObject类的多个Category分类中存在多个相同的方法
修复方式
移除多余的分类方法
重复的分类方法
扫描规则
NSObject原始类和类的Category分类中有相同的方法
修复方式
移除重复的分类方法
未实现的ObjC协议方法
扫描规则
类和分类未实现NSObject协议的非可选方法
可选的修复方式
对应的类实现缺失的非可选协议方法 将对应的协议方法标识为optional可选方法
重复的ObjC类
扫描规则
多个动态库和静态库之间存在同样的NSObject类符号
可能的修复方式
移除重复的类
性能
使用动态库
扫描规则
Macho为动态库
可选的修复方式
使用静态库 使用Mergeable Library
实现+load方法的类
扫描规则
实现+load方法的NSObject类
可选的修复方式
移除+load方法 使用+initialize替代
自定义配置
重要配置
systemFrameworkPaths
unusedObjCProperty-enable
开启未使用属性检查以后,会扫描macho的__TEXT段,会增加分析的耗时。
unusedClass-swiftEnable
开启Swift类检查以后,会扫描macho的__TEXT段,会增加分析的耗时。 未使用Swift类的项目建议不要开启,如果考虑执行性能的话Swift使用相对比较多的再开启。
提示:扫描macho的__TEXT段需要使用XCodeRun编译出的包,不能直接使用用于上架APP Store构建出的包。主要是Debug会包含更多的信息用于扫描。
配置属性
/Users/Test/APPAnalyzeCommand -ipa /Users/Desktop/ipas/APPMobile/APPMobile.app -config /Users/Desktop/ipas/config.json --output /Users/Desktop/ipas/APPMobile
{"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": [""] // 过滤掉名字符合正则表达式的资源}}}
组件化工程扫描
细化数据粒度 - 可以细化每个模块的包体积和包体积问题,更容易进行包体积优化。 更多的检查 - 例如检查不同组件同一个Bundle包含同名的文件,不同组件包含同一个category方法的的实现。 检查结果更准确 - 例如ObjC未使用方法的检查,只要存在一个和方法名同样的调用就表示方法有被使用到。但是整个ipa中可能存在很多一样的方法名但是只有一个方法有真正被调用到,如果细分到组件的粒度就可以发现更多问题。
提示:只有APP主工程无代码,全部通过子组件以framework的形式导入二进制库的方式的工程才适合这种模式。
扫描质量如何
和社区开源的工具有什么差异
扩展性不够 - 无法支持项目更好的扩展定制能力,例如添加扫描规则、支持不同类型扫描方式、生成更多的报告类型。 功能不全 - 只提供部分能力,例如只提供未使用资源或者未使用类。 无法生成包体积数据 - 无法生成包体积完整的数据。 检查质量不高 - 扫描发现的错误数据多,或者有一些问题不能被发现。
开源计划
开源带来的好处
扩展解析方式 - 目前只支持ipa模式扫描,很快会开放支持project组件化工程的扫描方式。基于组件化工程的扫描可以更加准确,但是不同的公司组件化工程的构建方式可能是不一样的,有需要可以在上层定制自身组件化工程的扫描解析。 扩展扫描规则 - 虽然现在已经添加了比较多的通用性的规则,同时提供了一定的灵活性配置能力。但是不同的项目可能需要定制一些其他的规则,这些规则没办法通过在现有规则上添加配置能力实现。 扩展数据生成 - 默认包里只包含两种数据生成,包体积数据还有包体积待修复问题数据。可以扩展更多的数据生成格式,例如我们自身的项目就有添加基于组件的依赖树格式。
后续规划
组件化工程支持
对于 Swift 更好的支持
未使用属性 - 编译器不会对于未使用属性进行移除,包括class和struct的属性。 未使用方法 - 对于class的方法,编译器并不会进行移除,即使没有申明@objc进行消息派发。