iOS APP package analysis tool

Share a script tool for analyzing iOS ipa packages. Using this tool, you can automatically scan and find fixable package volume problems, and at the same time, you can generate package volume data for viewing. This tool has been used within our team for a long time, and we hope it can help more developers optimize package size issues more efficiently.
Tool download address: https://github.com/helele90/APPAnalyze


1. Background

The APPAnalyze tool was first born mainly to solve the following package volume management problems:
For APPs targeting the sinking market, package size is a very important performance indicator. Excessive package size will affect users' willingness to download the APP. But in the early days, we lacked some means to help us manage package volume more efficiently.

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.


2. Implementation method

We chose to achieve this capability by not relying on source code but directly scanning binary libraries. The overall execution process is as follows:

Tip: The scanning method based on component engineering is supported internally, but it is not open to the public for the time being.



3. User Guide

Install

No installation required. Directly download the terminal executable command file APPAnalyzeCommand through the download link and use it locally.
APPAnalyzeCommand download address: https://github.com/helele90/APPAnalyze/releases

use

  
  
  
  
  
$ /Users/Test/APPAnalyzeCommand --helpOPTIONS:  --version <version>     当前版本 1.0.0  --output <output>       输出文件目录。必传参数  --config <config>       配置JSON文件地址。非必传参数  --ipa <ipa>             ipa.app文件地址。必传参数  -h, --help              Show help information.

implement

Open the terminal program and directly execute the following shell command to generate the ipa package volume data and the package volume issues to be repaired.

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

After the instruction is executed, the APPAnalyze folder will be generated in the folder specified by the ouput parameter. The specific documents are introduced as follows:

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

The defined classes are not used, including ObjC classes and Swift classes.
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

The ObjC protocol defined is not used by the class
Scan rules
  • The corresponding protocol is not referenced by the class
Optional repair methods
  • Remove unused protocols

Multiple Scale pictures in Bundle

Containing multiple Scales for the same image in a Bundle will result in a larger package size.
Scan rules
Optional repair methods
  • Remove images with lower Scale

Great resources

If the file size exceeds a certain size, it is considered a large resource. The default is 20KB.
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

There are multiple identical duplicate 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

Properties defined in the ObjC class are not used.
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

The included Imageset/DataSet is not used.
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

The defined ObjCCategory method is not used.
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

The defined ObjCCategory method is not used.
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

The included file resources are not used. The resources here do not contain Imageset/DataSet.
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

If the class name is consistent with the string, you may use the NSClassFromString() method to dynamically call the class. When strings or class names are changed, compile-time checks cannot be used to detect problems, which may lead to functional abnormalities.
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属性内存申明错误

一些特殊的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段需要使用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
可基于自身项目需要,添加下列规则可配置参数。在使用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": [""] // 过滤掉名字符合正则表达式的资源        }    }}

组件化工程扫描

可以基于APPAnalyzeCore.framework定制实现自己的组件化工程扫描,或者添加基于自身组件化工程的检查规则。详情可以看Demo。
基于组件化扫描方式有以下优势:
  • 细化数据粒度 - 可以细化每个模块的包体积和包体积问题,更容易进行包体积优化。
  • 更多的检查 - 例如检查不同组件同一个Bundle包含同名的文件,不同组件包含同一个category方法的的实现。
  • 检查结果更准确 - 例如ObjC未使用方法的检查,只要存在一个和方法名同样的调用就表示方法有被使用到。但是整个ipa中可能存在很多一样的方法名但是只有一个方法有真正被调用到,如果细分到组件的粒度就可以发现更多问题。

提示:只有APP主工程无代码,全部通过子组件以framework的形式导入二进制库的方式的工程才适合这种模式。



四、其他

扫描质量如何

这套工具我们团队内部开发加逐步完善有一年的时间了。基于此工具修改了几十个组件的包体积问题,同时不断的修复误报问题。目前现有提供的这些规则检查误报率是很低的,只有极少数几个规则可能存在误报的可能性,总体扫描质量还是很高的。

和社区开源的工具有什么差异

我们在早期调研了社区的几个同类型的开源工具,主要存在以下几个问题:
  • 扩展性不够 - 无法支持项目更好的扩展定制能力,例如添加扫描规则、支持不同类型扫描方式、生成更多的报告类型。
  • 功能不全 - 只提供部分能力,例如只提供未使用资源或者未使用类。
  • 无法生成包体积数据 - 无法生成包体积完整的数据。
  • 检查质量不高 - 扫描发现的错误数据多,或者有一些问题不能被发现。

开源计划

后续一定会开源。主要是希望调整一些内部结构再开源,开源后就不方便调整。顺便修复一些常见的问题。

开源带来的好处

开源带来的好处是,部分工程可以基于自身的业务需要,扩展定制自己的扫描工具。同时也可以将一些更好的想法实现添加进来。
  • 扩展解析方式 - 目前只支持ipa模式扫描,很快会开放支持project组件化工程的扫描方式。基于组件化工程的扫描可以更加准确,但是不同的公司组件化工程的构建方式可能是不一样的,有需要可以在上层定制自身组件化工程的扫描解析。
  • 扩展扫描规则 - 虽然现在已经添加了比较多的通用性的规则,同时提供了一定的灵活性配置能力。但是不同的项目可能需要定制一些其他的规则,这些规则没办法通过在现有规则上添加配置能力实现。
  • 扩展数据生成 - 默认包里只包含两种数据生成,包体积数据还有包体积待修复问题数据。可以扩展更多的数据生成格式,例如我们自身的项目就有添加基于组件的依赖树格式。

后续规划

组件化工程支持

添加更多用于组件化工程的扫描

对于 Swift 更好的支持

对于Swift语言只要开启XCode编译优化以后就能在生成产物的时候支持无用代码的移除,包括未使用类型和未使用方法的自动移除,但是依然有部分场景不会进行优化。所以这一块也是后续完善的重点:
  • 未使用属性 - 编译器不会对于未使用属性进行移除,包括class和struct的属性。
  • 未使用方法 - 对于class的方法,编译器并不会进行移除,即使没有申明@objc进行消息派发。

相关链接

Github地址:https://github.com/helele90/APPAnalyze
-end-

本文分享自微信公众号 - 京东云开发者(JDT_Developers)。
如有侵权,请联系 [email protected] 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

OpenAI 面向所有用户免费开放 ChatGPT Voice Vite 5 正式发布 运营商神操作:后台断网、停用宽带账号,强迫用户更换光猫 微软开源 Terminal Chat 程序员篡改 ETC 余额,一年私吞 260 余万元 Redis 之父用纯 C 语言代码实现 Telegram Bot 框架 假如你是开源项目维护者,遇到这种回复能忍到哪步? 微软 Copilot Web AI 将于12月1日正式上线,支持中文 OpenAI 前 CEO 和总裁 Sam Altman & Greg Brockman 加入微软 博通宣布成功收购 VMware
{{o.name}}
{{m.name}}

Guess you like

Origin my.oschina.net/u/4090830/blog/10151002