Automatically discover problems
-
Improve efficiency - Manual troubleshooting is inefficient. Common problems should be automatically scanned as much as possible. And for componentized projects, many external components are provided through the Framework, and there is no warehouse source code permission to analyze package volume issues. -
Processing - Form an automated quality process and add it to the CI pipeline to automatically detect package volume problems.
Quantification of data indicators
-
Package volume problem - Provides a data-based platform to view the package volume of each component to be fixed. -
Package size - provides a data-based platform to view the package size ratio of each component, including total size, single file binary size and size of each resource. Package volume data at component granularity can be compared for different APP versions, making it easier to view the component size increment of each version.
Tip: The scanning method based on component engineering is supported internally, but it is not open to the public for the time being.
Install
use
$ /Users/Test/APPAnalyzeCommand --help
OPTIONS:
--version <version> 当前版本 1.0.0
--output <output> 输出文件目录。必传参数
--config <config> 配置JSON文件地址。非必传参数
--ipa <ipa> ipa.app文件地址。必传参数
-h, --help Show help information.
implement
Tip: You cannot use AppStore packages directly. AppStore packages need to be shelled. It is recommended to use the XCodeDebug package as much as possible.
/Users/Test/APPAnalyzeCommand --ipa ipas/JDAPP/JDAPP.app --output ipas/JDAP
Tip: If it says permission denied, just execute sudo chmod -R 777 /Users/a/Desktop/ipas/APPAnalyzeCommand.
Generate products
Packet volume information
-
app_size.html - Displays the package volume data of each framework in ipa, which can be opened directly with a browser.
Tip: Divide the granularity according to the main program and dynamic library
-
framework_size.html - Displays all package volume data of a single framework. Do not open the secondary page directly.
Tip: When XCode generates Assets.car, it will splice some small pictures into a large picture of PackedAssetImage.
-
package_size.json - ipa package volume JSON data
Issues with package size to be fixed
-
app_issues.html - Displays the package volume of each framework in ipa and the number of issues to be fixed. It can be opened directly with a browser.
Tip: Divide the granularity according to the main program and dynamic library
-
framework_issues.html - Displays detailed data on all issues to be fixed in a single framework. Do not open the secondary page separately.
-
issues.json - JSON data of ipa package volume issues to be repaired
Tip: json data can be used to build your own data platform and expand more capabilities. For example, view different APP versions and support comparison of multiple APP versions.
Rule introduction
Packet volume
unused class
Scan rules
-
No corresponding ObjC class was found to be referenced. -
Not used as a parent class -
Unused strings and class names are consistent -
Not used as an attribute type -
No method was created or called -
The +load method is not implemented
Optional repair methods
-
Remove unused classes -
If the Swift class only uses static methods, consider changing it to the Enum type. -
If it is only used during type conversion, it will also be detected as an unused class, such as (ABCClass *)object;. It is recommended to check whether there are really relevant classes and then delete them. -
For ObjC, if it is only used as a method parameter type, it will also be detected as an unused class. It is recommended to delete the relevant methods.
Tip: Deleting a class is relatively safe because a compile-time error will occur if it is used after deletion. Although there is scanning and filtering for string calls, it is still recommended to check whether it may be dynamically created and called by Runtime.
Unused ObjC protocols
Scan rules
-
The corresponding protocol is not referenced by the class
Optional repair methods
-
Remove unused protocols
Multiple Scale pictures in Bundle
Scan rules
-
The same Bundle contains images with the same name but different scales. For example [email protected]/[email protected]
Optional repair methods
-
Remove images with lower Scale
Great resources
Scan rules
-
A certain file exceeds the set maximum resource limit
Optional repair methods
-
Remove dynamic delivery of resources -
Use smaller data formats, such as smaller image formats
Duplicate resource files
Scan rules
-
If multiple files have the same MD5, they are determined to be duplicate files.
Optional repair methods
-
Remove redundant files
Unused class Property attribute
Scan rules
-
The corresponding attribute has not been called the set/get method, and it has not been used in the _ way. -
Not a property from the implementation protocol -
Not an attribute from Category -
There is no string usage consistent with the attribute name.
Optional repair methods
-
Remove the corresponding attribute -
If it is an attribute of an interface protocol, you need to add a class to implement this interface
Precautions
-
There may be some dynamic usage scenarios that require certain checks. For example, some data model classes that inherit NSObject may have attributes that are not used directly, but may be called JSON as parameters. For example, the data model issued by the background
Unused ImageSet/DataSet
Scan rules
-
The string with the same name as Imageset was not detected.
Optional repair methods
-
Remove ImageSet/DataSet
Precautions
-
Some strings used in Swift code cannot be found and are treated as unused. -
Use the name of string concatenation as the name of the imageset. -
The Imageset synthesized into PackedAssetImage cannot be scanned out
Unused ObjC method
Scan rules
-
There is no method name identical to this method. -
The string used does not match the method name. -
Method not from parent class or Category -
Not from a method that implements an interface -
Not an attribute set/get method
Optional repair methods
-
Remove the corresponding method
Unused classification method
Scan rules
-
There is no method name identical to this method. -
There is no string usage consistent with the method name. -
Method not from parent class or Category -
Not from a method that implements an interface
Optional repair methods
-
Remove unused methods -
If it is a method of an interface protocol, you need to add a class to implement this interface
unused resource files
Scan rules
-
No string usage with the same name as the file name was detected.
Optional repair methods
-
Remove resources
Precautions
-
Strings used in some Swift code cannot be found and are treated as unused -
Use string concatenation as the name of the resource
Safety
Dynamic reflection call ObjC class
Scan rules
-
The string used is the same as the NSObject subclass class name.
Optional repair methods
-
Use NSStringFromClass() to get the class name string -
Classes outside the Framework should be encapsulated using methods. Except for a few functions, reflection should not be used to call the class.
Tip: Contain swift classes that inherit NSObject.
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进行消息派发。
相关链接
本文分享自微信公众号 - 京东云开发者(JDT_Developers)。
如有侵权,请联系 [email protected] 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。