PrefacePackage size is an important indicator for measuring APP performance. It directly affects the user's download click rate (the package is too big and you don't want to download it), download and installation success rate (download slowly and don't use it), and APP uninstall rate (delete it first if it takes up too much space). The calculation logic of package size is very simple. It is the sum of the disk size occupied by various types of files. The technology of APP slimming is very complicated. The complexity of the code file and the compiler strategy determine the size of the executable file, and the business function and engineering architecture determine the complexity of the code file. For iOS APP slimming, the skills that need to be mastered include XCode build technology, LLVM compiler technology, CocoaPods build technology, image compression technology, and continuous integration technology. This article summarizes and refines the slimming technology and strategy of Alibaba.com App, and systematically introduces the business value, analysis technology, slimming technology, and anti-degradation mechanism of APP slimming, so that readers can systematically understand the technical system of APP slimming. Based on practical experience, this article also introduces the ROI of various slimming technologies, so that readers can avoid stepping on mines and wasting resources on technologies with poor results. I hope it will be helpful to you. Business ValueFor every 6MB increase in package size, the app download conversion rate decreases by 1%.At the 2019 Google Developer Conference, Google gave a very detailed data, every 6MB increase in package size, the app download conversion rate will drop by 1%. The conversion rates in different regions vary slightly. For every 10MB reduction in APK package size, the global average download conversion rate will increase by 1.75%, the download conversion rate of emerging countries such as India and Brazil will increase by more than 2.0%, and the download conversion rate of high-end markets such as the United States and Germany will increase by 1.5%. Title of the above picture: APK reduced by 10MB, conversion rate increased in different countries. Data source: Google Play internal data. Detailed information: White Whale goes to sea: Highlights of the first day of the 2019 Google Developer Conference. The above data survey and analysis report was before 2019 and is already somewhat lagging behind, so it is for reference only. There are three possible reasons why package size affects download conversion rate:
Although Google Play does not provide data for different APP categories, based on the above three reasons, it is estimated that the impact of package sizes of different categories on download conversion rates is similar. The user population of the App Store is relatively high-end, and the data of the United States and Germany can be used as a reference. 20% of people uninstall apps due to limited storage spaceCleverTap conducted a survey in 2021 in which they surveyed more than 2,000 mobile app users and asked them about the main reasons for uninstalling mobile apps. 20% of them uninstalled apps due to limited storage space. The three main reasons are:
Detailed material: clevertap: Why Users Uninstall Apps. App Store Distribution and Download RestrictionsFor iOS8-compatible apps, the text segment of the main binary file cannot exceed 60MB, otherwise it will not be submitted to the App Store. If the App Store download package exceeds 200MB, it cannot be downloaded and updated using cellular data. Analytical techniquesThe ultimate goal of APP slimming is to reduce the size of the App Store installation package and the download package size, but it is more convenient to compare the XCode build package size during the R&D phase, and the differences in caliber between them need to be clarified. Result indicators: App Store installation package size and download package sizeThe viewing path is App Store Connect->TestFlight->Delivery version->Build version metadata->App Store file size. Process Indicator: XCode Build SizeThe size of the ipa package built by XCode and the installation size of the App Store are very different. The relationship between them can be obtained by simulating the process processed by Apple. The ipa package uploaded by the developer contains products of multiple architectures and multiple sets of image resources of different sizes. Apple will crop and redistribute them and convert them into ipa packages downloaded from the App Store. The size of the iPad package of the built product: static library binary files (arm64, armv7), dynamic libraries (arm64, armv7), asset.car (@2x, @3x), and other resource files. App Store installation size: static library binaries (single architecture), dynamic libraries (single architecture), asset.car (single size), other resource files. Use the lipo tool to split a single architecture.lipo "originalExecutable" - thin arm64 - output "arm64Executable" Use the assetutil tool to split assets xcrun --sdk iphoneos assetutil --scale 3 --output "$targetFolder/Assets3.car" "$sourceFolder/Assets.car" Build package compositionBefore learning how to cure diseases, we must first understand the composition of the human body. Similarly, we must first understand the iOS project structure and IPA package structure. iOS project structure: iOS project consists of shell project and Pod module. There are two types of modules: static library and dynamic library. Shell project consists of main Target and Apple plug-in Target. The internal structure of the module includes source code files (OC, C, C++.h and.m), nib, bundle, xcassets, multi-language files, and various configuration files (plist, json). IPA package structure: iOS uploaded to App Store is an IPA package. After decompression, the IPA package is a folder, which consists of various types of files, mainly including MachO executable files, .framework (dynamic library), Assets.car, .appex (Apple plug-in), .strings (multi-language), .bundle, nib, json, png... From iOS project to IPA package: iOS project is built as IPA package. The core changes are compilation and file copying. The source code in the static library will be compiled into MachO executable files, and the xcassets folder will be converted into Assets.car. The others can be simply understood as file copies. By analyzing the composition of the build package, you can determine where there is room for optimization. If the dynamic library accounts for too high a proportion, there may be a large amount of code that can be cut. It is recommended to divide it according to the sensitivity of resource size, such as: MachO executeable (including all static libraries), dynamic library, Apple Extension, assets.car, bundle, nib, audio, video. Pod module sizeAPP slimming involves a large number of business modules and cannot be separated from the participation of the business team. Therefore, we need to analyze the size of each Pod module so that we can compare the package size ratio of each business team horizontally. The calculation principles of static libraries and dynamic libraries are different. For static libraries, first parse the linkmap data to calculate the code size of the Pod module, then parse the resource copy code of Pods-targetName-resource.sh to calculate the size of the resources copied to the Pod module. For dynamic libraries, first use lipo to split the binary files of the dynamic library, calculate the code size of a single architecture, and then calculate the resource files in the dynamic library framework to get the resource file size of the dynamic library. Runtime Objc class coverageIf we know which classes have been used when the App is running, we can take down useless modules or code files. The Objc class coverage indicator can help us. When the APP is running, the number of classes loaded by a Pod module divided by the number of all classes can be called the Objc class coverage of this module. The core technology is to determine whether a class has been loaded. Here is a lightweight solution that has been verified online. The +initialize method is called when the ObjC class is used for the first time. After the class is loaded, cls->isInitialized will return True. The isInitialized method reads the flags in the data variable of metaClass and returns True if the 29th bit in flags is 1. // objc - class .mm Unused image resourcesThe technical principle is to first parse out all the resource files copied to the build product, and then parse out the resource files actually referenced in the code. The difference between the two is the useless resources. The first step is to obtain all resource files. In the Cocoapos project, the "Pods-targetName-resource.sh" script is responsible for copying the file resources in the Pod to the build product, including all file types bundle, xcassets, json, png. Parsing this script can get which image resources are copied by each Pod module. // Pods - targetName - resource .sh The second step is to get the resource files actually referenced in the code. The resource files referenced in the OC code are declared in the form of string literals and stored in the "__cstring" section of the Mach-O file after construction. By using strings to parse the binary file of the framework, you can get all the string declarations in the code, and then match "xxx", "[email protected]", "xxx.png", "xxx.bundle/xxx.png" based on various reference methods of the code. strings executable | grep 'xxx' > cstrings .txt Classes not referenced at compile timeThe product of iOS compilation is in Mach-o format. The __DATA __objc_classrefs segment in the file records the addresses of all referenced classes, and the __DATA __objc_classlist segment records the addresses of all classes. Differing the two can get the addresses of unreferenced classes. Then symbolize the addresses to get the information of unreferenced classes. Because Objc is a dynamic language, if a class is dynamically called using runtime, this situation cannot be scanned. (For example, Target Action and JS Core). otool - v - s __DATA __objc_classrefs xxxMainClient #Read the symbol with section __objc_classrefs in __DATA Segment Open source tool to scan for unused classes. Weight loss technologyPackage size reduction can be achieved from a purely technical perspective or from a logical perspective. From a purely technical perspective, there are two approaches. The first is to optimize the compilation logic. The second is to delete various other types of files (non-compilation products). Compilation optimization is non-invasive to business logic, with relatively low risks and costs, but the benefits are usually not high. Deleting files is more complicated. Deleting resource files has high benefits but high costs. Deleting source code files one by one has high risks and low benefits. In comparison, the slimming effect will be more obvious from the perspective of logic. From the perspective of logic, the project is composed of many functional modules, which can be mainly divided into business functional modules (home page, search, cashier, order), basic functional modules (network library, image library, middleware, various third-party libraries...). Large projects usually consist of hundreds or even thousands of modules, small modules are tens of KB, and large modules are 1MB~10+MB. Functional modules have strong cohesion. When a functional module can be discarded or replaced, the overall risk and cost of offline are relatively low, and the ROI is very high. Slimming technology mapBased on project experience, I have sorted out the ROI of various optimization methods, which are introduced below. Component SlimmingROI of component slimming: Components with 0% class loading rate > useless functional components > duplicate functional components "Components with 0% class loading rate" is a complete Pod module, the module code and resources can be deleted as a whole, and the ROI is the highest. "Useless functional components" are usually a file directory within the module, which requires some coupling processing and resource ownership sorting, and the ROI is second. "Duplicate functional components" need to be properly refactored, with the highest migration cost and risk, and a relatively low ROI. Components with 0% class loading rate"Components with 0% class loading rate" means that no class has been loaded during operation. It is completely unused during operation and can be taken offline as a whole. When class loading rate statistics are generally collected, sampling is performed. Some low-frequency business components may be misreported. You need to confirm with the business owner before taking it offline. Useless functional components"Useless functional components" are components that are no longer useful logically. They may still have coupling in the code, but the logic is no longer useful and can be taken offline through refactoring. Examples include business codes that have no effect in AB experiments, business codes from previous years' big promotions, old businesses left over after business revisions, and outdated third-party libraries. Repeating functional componentsFor large-scale teams, different business lines may introduce "duplicate functional components", such as image selectors, cache libraries, and UI components. Duplicate functional components will bring unnecessary package size pressure and maintenance costs. Resource reductionResource slimming ROI: large resources > lossy compression > duplicate resources > iconfont > multi-language text slimming > useless resources. Great resourcesSingle-point optimization of large resources is very profitable, and resources larger than 100KB are analyzed first. For example, the audio file in our project is 900KB, and 700KB is removed after optimization. Lossy CompressionWhen XCode is built, it will "compile asset catalog" and re-compress the images losslessly. Therefore, the effect of lossless compression using tools such as imageoptim is not obvious. There is no effect on compressing png images, but there is a certain effect on compressing jpg images. According to practical experience, lossy compression of icons does not affect the visual experience, and the compression rate can reach 70%~80%. For example, using the tinypng algorithm to compress a 425.9 KB png image, the compressed image is 79.8 KB, and the compression rate reaches 81%. There are many png compression tools in the industry, and we use tinypng, pngquant, pngcrush, optipng (lossless), and advpng. It is found that different tools have different compression effects for different images, so when compressing images, you can try each tool and then take the best one. It should be noted that compressed APNG images may become non-animated images. Try to avoid compressing APNG images and identify whether they are APNG images before compression. # Determine whether it is APNG format, APNG format is not compressed Useless resourcesLong-term business iteration will accumulate many useless resources. Through code static scanning, you can analyze the image resources that are not referenced. We have 12MB of useless resources in the project, of which 20 modules have useless resources exceeding 100KB. Repeat IconDifferent business requirements will introduce duplicate resources, which will cause great waste in the long run. The solution is to calculate the hash value of the resource during the build, remove the same hash resources, and keep the mapping table of source file names and hash values. Hook the "imageNamed" method of resource loading at runtime and replace the resource name according to the mapping table. ODRiOS cannot update the Framework at runtime like Android. iOS's ODR technology (On Demand Resource) provides the ability to dynamically download resources at runtime. If resource files are not needed at startup, they can be processed through ODR. Icon fontIconfont supports scaling and color modification. It is small in size and suitable for icon scenes such as arrows and placeholders. Using iconfont can reduce the package size and improve the uniformity of the development visual experience. First, a complete set of iconfonts is developed based on the design specifications, the iconfont components are encapsulated, and an easy-to-use API is provided. Then, it is forbidden to add image resources for new business scenarios. After the team forms development habits, the existing images can be gradually replaced. Multilingual copywritingFor international apps, multilingual copywriting is also a big part. Our app supports 20 languages, with more than 4,000 copywritings in each language, and the total copywriting size is 6MB, which was reduced by 2MB after slimming down.
Streamlined compilation product Oz: Optimization LevelThere are multiple optimization levels. -Oz is about 10% smaller than -O3. After setting -Oz, XCode will optimize consecutive assembly instructions to reduce the binary size, but the side effect is that the execution speed will be slower. C++ projects are recommended to enable both. Main Project Release LTO (OC/C++)LTO is to optimize cross-module calling code when LLVM is linked. According to everyone's experience, the optimization effect of different APP package sizes varies greatly, and specific testing is required. In addition, it also has some side effects, so it is recommended to only enable it in the Release package. LTO Introduction: advantage:
Main Project Release Dynamic library reuses main binary static libraryC++ dynamic libraries often use some basic libraries such as openssl, libyuv, and libcurl, which are generally static libraries. If a dynamic library references a static library, all the symbols of the static library will be embedded by default when it is compiled. Although we can set the dynamic library to export only the static library symbols that need to be used, it is possible that multiple dynamic libraries use the same basic library, which will still cause redundancy of the basic library. For example, the size of openssl is 1MB. If the two dynamic libraries A and B depend on openssl, and the APP also references openssl, the final ipa package actually has 3 openssl, and 2MB of it is redundant. In this scenario, the best solution is to share the symbol table so that the dynamic library can call the basic library symbols of the main binary, so that the built-in static library can be removed. Just modify the Link configuration of XCode, no additional code development is required. Dynamic library project:
Other Linker Flags -> - undefined dynamic_lookup 3 Export the external symbols that the dynamic library needs to call and write them to a file exported_symbols. nm - u xxx .framework / xxx > exported_symbols .txt APP Engineering:
// exported_symbols.txt is the path to the symbol file that needs to be exported If a C++ dynamic library depends on an external static library, all symbols of the static library will be exported by default. In fact, the dynamic library only uses some of the symbols. You can set a whitelist of exported symbols to reduce the number of unused static library symbols exported. Notes: 1. Both APP and framwork projects must turn off bitcode, otherwise they will not compile. 2. If multiple dynamic libraries use the same basic library, the exported symbol tables need to be combined. 3. When upgrading dynamic libraries, the symbol tables must be updated in time, and when upgrading basic libraries, the compatibility must be tested. Linker product compression (black technology)The product of iOS project construction is MachO file. The TEXT segment in MachO file stores various read-only data segments, the __cstring segment stores ordinary C string, and __objc_methtype and __objc_methname store Objc method signature and method name. For example, if @"Hello world" is declared in Objc code, a CFString will be generated at the bottom layer, which will be stored in __cstring after construction. These data take up a lot of space. Generally, the project will have at least 10MB, so the benefits of compression are considerable. After we went online, the size of the App Store installation package was optimized from 191MB to 174MB, a reduction of 16MB. Technical principle: When linking, the TEXT segment data is moved to the __DATA segment and compressed. When running, the decompression code is executed first, the decompressed TEXT segment data is stored in the custom segment, and the address of the string reference in the code is corrected to the decompressed custom segment. Strip Linked ProductThe Strip Linked Product setting will strip specific symbols. Do not set it to YES in the Debug environment, otherwise the symbols will not be visible during debugging. Main Project Release illustrate:
Symbols Hidden by DefaultSymbols Hidden by Default is used to set the default visibility of symbols. If it is set to YES, XCode will define all symbols as "private extern", and the package size will be slightly reduced. Set it to NO for dynamic libraries, otherwise there will be link errors. Main Project Release Remove unreferenced C/C++/Swift code: Dead Code StrippingWhen Dead Code Stripping is turned on, unused code will be removed during linking. It is effective for static languages C/C++/Swift, but not for dynamic languages OC. Main Project Asset Catalog CompilerOptimization has three options: empty, time, and Space. Select Space to optimize the package size. Main Project C++ only exports necessary symbols: Symbol VisibilityBoth C++ static and dynamic libraries export only necessary symbols. The default setting is to hide all symbols, and then use Visibility Attributes to individually control the symbols that need to be exported. Hide all C++ symbols by default Other C ++ Flags -> Add -fvisibility = hidden Set the symbols to be exported __attribute__ ( ( visibility ( "default" ) ) ) void MyFunction1 ( ) { } Code offlineAccording to the results of the ObjC class coverage statistics, unused classes can be gradually offline. The code files are relatively small, and the offline code needs to be carefully confirmed to avoid functional problems or crashes, and the ROI is relatively low. Flutter ProjectFlutter is built separately, and the content introduced includes Flutter engine products and Flutter business code products. If an APP introduces Flutter, Flutter needs to be specially slimmed down. Streamlined compilation product Oz: Optimization LevelCompared with Os, the -Oz option is estimated to have an 11% gain, but a 1% to 9% loss in first screen performance. Dart symbol stripping and obfuscationAccording to the official documentation, you can use the --dwarf-stack-trace option to remove Dart standard debugging symbols. With the --obfuscate option, you can replace longer symbols with shorter symbols, but the side effect is that the symbols will be obfuscated. Practical effect: In the Release environment, the size is reduced by 14% after the --dwarf-stack-trace and --obfuscate options are turned on. Flutter icon tree shaking optimization--tree-shake-icons practical effect: The size of Alibaba.com App is reduced by about 300KB after opening. Remove NOTICES filesPractical effect: 700KB before compression, 80KB after compression. Anti-degradation mechanismIncremental standard and integrated bayonetThe technology of package size slimming is like various weight loss exercises, such as running, dancing, and fat burning yoga. However, if you want to achieve the goal of weight loss, exercise alone is not enough. You must also control your calorie intake. Only those who control their diet can finally lose weight successfully. Therefore, we also need "additional calorie specifications" (package size increment specifications) and supervisors (integration checkpoints). Add a checkpoint plug-in to the continuous integration, analyze the linkemap file of the build package to get the module size, and then compare it with the baseline data. If the package size increment standard is violated, integration is prohibited. Packet size increment specification (reference):
Compare business health horizontallyWhen the package size has been optimized to the extreme from a technical perspective, if you want to further reduce it, you can only explore from the perspective of business value. If a module has a large disk size but few users use it, it can be considered that its value to the business is small. Therefore, we can define a technical indicator to measure the business contribution of the module unit size. Based on the floor area ratio indicator, we can compare various businesses horizontally and require businesses with low floor area ratios to reduce package size or remove less important business functions. Floor area ratio = Business PV / Business size. SummarizeLet's summarize the implementation path of package size reduction. The first step is to set goals and track the result indicators such as APP download conversion rate (App Store Connect Analytics), APP installation package size, XCode build package size, etc. The second step is to build an analysis system, including Pod module size analysis, Objc class coverage analysis, useless image resource analysis, etc. The third step is to use various slimming technologies according to ROI, component slimming > resource slimming > compilation optimization > code offline. The fourth step is to build an anti-degradation mechanism, including incremental standards, integrated card slot capabilities, and health analysis. References:White Whale goes to sea: Highlights of the first day of the 2019 Google Developer Conference: New changes in Google Play: http://www.baijingapp.com/article/24808 Open source tool for scanning unused classes: https://github.com/xuezhulian/classunref googleplaydev:Shrinking APKs, growing installs https://medium.com/googleplaydev/shrinking-apks-growing-installs-5d3fcba23ce2 clevertap:Why Users Uninstall Apps https://clevertap.com/blog/uninstall-apps/ Pngcrush: https://pmt.sourceforge.io/pngcrush/ pngquant:https://pngquant.org/ OptiPNG: http://optipng.sourceforge.net/ advpng: http://www.advancemame.it/doc-advpng.html LTO Introduction: https://zhuanlan.zhihu.com/p/384160632 |
<<: Component Library Design Guide: The Birth of a Component Library
>>: Switching between HStack and VStack in SwiftUI
Li Mengchao interprets Karen: Talk to your inner ...
Just a while ago, a popular whole-wheat bread &qu...
To improve the document navigation experience for...
Meizu, which changed its market strategy this yea...
Wood type printing Author: Lei Biyu Source: "...
Everyone knows that Wu Dalang from Qinghe County,...
At the 2016 Guangzhou Auto Show, SAIC Roewe relea...
In modern society, many high-techs are no longer h...
This article was reviewed by: Xiaobo Zhou, Doctor...
During the just-passed National Day holiday, Yunn...
Friends Have you found Many northerners around me...
When visiting a museum, if you pay close attentio...
With the development of the Internet, it can even...
Summary of advertising placement on 7 major chann...
iOS WeChat released version 6.6.0 today, which br...