Herramienta de análisis de paquetes de aplicaciones iOS

Comparta una herramienta de secuencia de comandos para analizar paquetes ipa de iOS. Con esta herramienta, puede escanear y encontrar automáticamente problemas de volumen de paquetes que se pueden solucionar y, al mismo tiempo, puede generar datos de volumen de paquetes para su visualización. Esta herramienta se ha utilizado en nuestro equipo durante mucho tiempo y esperamos que pueda ayudar a más desarrolladores a optimizar los problemas de tamaño de los paquetes de manera más eficiente.
Dirección de descarga de la herramienta: https://github.com/helele90/APPAnalyze


1. Antecedentes

La herramienta APPAnalyze nació principalmente para resolver los siguientes problemas de gestión de volumen de paquetes:
Para las aplicaciones dirigidas al mercado en hundimiento, el tamaño del paquete es un indicador de rendimiento muy importante. Un tamaño excesivo del paquete afectará la disposición de los usuarios a descargar la aplicación. Pero al principio, carecíamos de algunos medios que nos ayudaran a gestionar el volumen de paquetes de forma más eficiente.

Descubrir problemas automáticamente

  • Mejore la eficiencia: la resolución manual de problemas es ineficiente. Los problemas comunes deben analizarse automáticamente tanto como sea posible. Y para los proyectos en componentes, muchos componentes externos se proporcionan a través del marco y no existe ningún permiso de código fuente del almacén para analizar problemas de volumen de paquetes.
  • Procesamiento: forme un proceso de calidad automatizado y agréguelo al proceso de CI para detectar automáticamente problemas de volumen de paquetes.

Cuantificación de indicadores de datos.

  • Problema de volumen de paquetes: proporciona una plataforma basada en datos para ver el volumen de paquetes de cada componente que se va a reparar.
  • Tamaño del paquete: proporciona una plataforma basada en datos para ver la proporción del tamaño del paquete de cada componente, incluido el tamaño total, el tamaño binario de un solo archivo y el tamaño de cada recurso. Los datos del volumen del paquete en la granularidad del componente se pueden comparar para diferentes versiones de la aplicación, lo que facilita ver el incremento del tamaño del componente de cada versión.


2. Método de implementación

Elegimos lograr esta capacidad sin depender del código fuente, sino escaneando directamente la biblioteca binaria. El proceso de ejecución general es el siguiente:

Consejo: El método de escaneo basado en la ingeniería de componentes es compatible internamente, pero no está abierto al público por el momento.



3. Guía del usuario

Instalar

No requiere instalación. Descargue directamente el archivo de comando ejecutable del terminal APPAnalyzeCommand a través del enlace de descarga y utilícelo localmente.
Dirección de descarga de APPAnalyzeCommand: https://github.com/helele90/APPAnalyze/releases

usar

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

implementar

Abra el programa de terminal y ejecute directamente el siguiente comando de shell para generar los datos del volumen del paquete ipa y los problemas del volumen del paquete que se van a reparar.

Consejo: No puedes utilizar los paquetes de la AppStore directamente. Los paquetes de la AppStore deben ser desgranados. Se recomienda utilizar el paquete XCodeDebug tanto como sea posible.

/Users/Test/APPAnalyzeCommand --ipa ipas/JDAPP/JDAPP.app --output ipas/JDAP

Consejo: si dice permiso denegado, simplemente ejecute sudo chmod -R 777 /Users/a/Desktop/ipas/APPAnalyzeCommand.

Generar productos

Después de ejecutar la instrucción, la carpeta APPAnalyze se generará en la carpeta especificada por el parámetro de salida. Los documentos específicos se presentan de la siguiente manera:

Información del volumen del paquete

  • app_size.html: muestra los datos del volumen del paquete de cada marco en ipa, que se pueden abrir directamente con un navegador.

Consejo: divida la granularidad según el programa principal y la biblioteca dinámica


  • framework_size.html: muestra todos los datos del volumen del paquete de un solo marco. No abra la página secundaria directamente.

Consejo: cuando XCode genera Assets.car, unirá algunas imágenes pequeñas en una imagen grande de PackedAssetImage.

  • package_size.json - datos JSON del volumen del paquete ipa

Problemas con el tamaño del paquete por solucionar

  • app_issues.html: muestra el volumen del paquete de cada marco en ipa y la cantidad de problemas que se solucionarán. Se puede abrir directamente con un navegador.

Consejo: divida la granularidad según el programa principal y la biblioteca dinámica

  • framework_issues.html: muestra datos detallados sobre todos los problemas que se deben solucionar en un solo marco. No abra la página secundaria por separado.
  • issues.json: datos JSON de problemas de volumen del paquete ipa que se van a reparar

Consejo: los datos json se pueden utilizar para crear su propia plataforma de datos y ampliar más capacidades. Por ejemplo, vea diferentes versiones de aplicaciones y admita la comparación de múltiples versiones de aplicaciones.

Introducción a la regla

Volumen de paquetes

clase no utilizada

Las clases definidas no se utilizan, incluidas las clases ObjC y las clases Swift.
Reglas de escaneo
  • No se encontró referencia a ninguna clase ObjC correspondiente.
  • No se utiliza como clase principal
  • Las cadenas no utilizadas y los nombres de clases son consistentes
  • No se utiliza como tipo de atributo
  • No se creó ni llamó ningún método
  • El método +load no está implementado
Métodos de reparación opcionales
  • Eliminar clases no utilizadas
  • Si la clase Swift solo usa métodos estáticos, considere cambiarla al tipo Enum.
  • Si solo se usa durante la conversión de tipos, también se detectará como una clase no utilizada, como (ABCClass *)object;. Se recomienda comprobar si existen clases realmente relevantes y luego eliminarlas.
  • Para ObjC, si solo se usa como tipo de parámetro de método, también se detectará como una clase no utilizada. Se recomienda eliminar los métodos relevantes.

Consejo: Eliminar una clase es relativamente seguro porque se producirá un error en tiempo de compilación si se usa después de eliminarla. Aunque se escanean y filtran llamadas de cadenas, se recomienda verificar si Runtime puede crearlas y llamarlas dinámicamente.

Protocolos ObjC no utilizados

El protocolo ObjC definido no es utilizado por la clase.
Reglas de escaneo
  • La clase no hace referencia al protocolo correspondiente.
Métodos de reparación opcionales
  • Eliminar protocolos no utilizados

Múltiples imágenes a escala en paquete

Si contiene varias escalas para la misma imagen en un paquete, el tamaño del paquete será mayor.
Reglas de escaneo
Métodos de reparación opcionales
  • Eliminar imágenes con escala más baja

Grandes recursos

Si el tamaño del archivo excede un cierto tamaño, se considera un recurso grande y el valor predeterminado es 20 KB.
Reglas de escaneo
  • Un determinado archivo excede el límite máximo de recursos establecido
Métodos de reparación opcionales
  • Eliminar la entrega dinámica de recursos
  • Utilice formatos de datos más pequeños, como formatos de imagen más pequeños.

Archivos de recursos duplicados

Hay varios archivos duplicados idénticos.

Reglas de escaneo

  • Si varios archivos tienen el mismo MD5, se determina que son archivos duplicados.
Métodos de reparación opcionales
  • Eliminar archivos redundantes

Atributo de propiedad de clase no utilizada

Las propiedades definidas en la clase ObjC no se utilizan.
Reglas de escaneo
  • El atributo correspondiente no se ha llamado método set/get y no se ha utilizado de la manera _.
  • No es una propiedad del protocolo de implementación.
  • No es un atributo de la categoría
  • No hay ningún uso de cadena coherente con el nombre del atributo.
Métodos de reparación opcionales
  • Eliminar el atributo correspondiente
  • Si es un atributo de un protocolo de interfaz, debe agregar una clase para implementar esta interfaz
Precauciones
  • Puede haber algunos escenarios de uso dinámico que requieran ciertas comprobaciones. Por ejemplo, algunas clases de modelos de datos que heredan NSObject pueden tener atributos que no se usan directamente, pero pueden denominarse JSON como parámetros. Por ejemplo, el modelo de datos emitido en segundo plano.

Conjunto de imágenes/conjunto de datos no utilizados

El conjunto de imágenes/conjunto de datos incluido no se utiliza.
Reglas de escaneo
  • No se detectó la cadena con el mismo nombre que Imageset.
Métodos de reparación opcionales
  • Eliminar conjunto de imágenes/conjunto de datos
Precauciones
  • Algunas cadenas utilizadas en el código Swift no se pueden encontrar y se tratan como no utilizadas.
  • Utilice el nombre de la concatenación de cadenas como nombre del conjunto de imágenes.
  • El conjunto de imágenes sintetizado en PackedAssetImage no se puede escanear

Método ObjC no utilizado

No se utiliza el método ObjCCategory definido.
Reglas de escaneo
  • No existe ningún nombre de método idéntico a este método.
  • La cadena utilizada no coincide con el nombre del método.
  • Método que no pertenece a la clase principal o categoría
  • No de un método que implementa una interfaz.
  • No es un método de conjunto/obtención de atributos
Métodos de reparación opcionales
  • Eliminar el método correspondiente

Método de clasificación no utilizado

No se utiliza el método ObjCCategory definido.
Reglas de escaneo
  • No existe ningún nombre de método idéntico a este método.
  • No hay ningún uso de cadena coherente con el nombre del método.
  • Método que no pertenece a la clase principal o categoría
  • No de un método que implementa una interfaz.
Métodos de reparación opcionales
  • Eliminar métodos no utilizados
  • Si es un método de un protocolo de interfaz, debe agregar una clase para implementar esta interfaz

archivos de recursos no utilizados

Los recursos de archivos incluidos no se utilizan. Los recursos aquí no contienen Imageset/DataSet.
Reglas de escaneo
  • No se detectó ningún uso de cadena con el mismo nombre que el nombre del archivo.
Métodos de reparación opcionales
  • Eliminar recursos
Precauciones
  • Las cadenas utilizadas en algunos códigos Swift no se pueden encontrar y se tratan como no utilizadas
  • Utilice concatenación de cadenas como nombre del recurso

Seguridad

Llamada de reflexión dinámica clase ObjC

Si el nombre de la clase es coherente con la cadena, puede utilizar el método NSClassFromString() para llamar dinámicamente a la clase. Cuando se cambian cadenas o nombres de clases, las comprobaciones en tiempo de compilación no se pueden utilizar para detectar problemas, lo que puede provocar anomalías funcionales.
Reglas de escaneo
  • La cadena utilizada es la misma que el nombre de la clase de subclase NSObject.
Métodos de reparación opcionales
  • Utilice NSStringFromClass() para obtener la cadena del nombre de la clase
  • Las clases fuera del Framework deben encapsularse mediante métodos y, excepto algunas funciones, no se debe utilizar la reflexión para llamar a la clase.

Consejo: contenga clases rápidas que hereden 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}}

Supongo que te gusta

Origin my.oschina.net/u/4090830/blog/10151002
Recomendado
Clasificación