The process of iOS compilation can be simplified as follows: preprocessing – compilation generating intermediate code – assembler generating assembly code – generating machine code – linking – generating executable files. When packaging, we can simply think of the packaging time as: compile time + link time + generate debugging information time. Since the link is not cached and can only be done on a single core, its time depends on single-core performance and disk read/write speed, so there is little room for optimization other than device replacement. For debugging information, our daily test package can choose not to generate debugging information (dSYM file), which can save about 20 seconds. As you can see, most of the time we can reduce is in the compile phase.

Reduce compiled files and resources

Since all the dependent files, system libraries and third-party libraries will be introduced into the compilation process, a more direct way is to reduce the number of files and resources, keep the code clean, and clean up the libraries and files that are not needed in time.

The picture below is part of the dependency system library of the car dealer, and you can see that there are also Twitter, so there will be a lot of redundant dependencies in the case of files accumulated over time.

2. Reduce duplicate compilation

In existing projects, we use #import to import dependent files. Since #import essentially copies the contents of the dependent header into its own header file, header A actually contains M N headers, which requires M N file IO and related processing. The M * N complexity process is repeated each time A file that depends on header A is added to the project.

We used to use PCH files. The benefit of the PCH file is that the header files in this file are compiled and cached only once, and then added to all the header files in the project. The above problem is solved, but the stupid thing is that all files are implicitly dependent on all files in the PCH, and the number of files that really need to be globally dependent is very few. As a result, in practice, more people see PCH as a means of fast import than as a compilation performance optimization. As explained earlier, a change to a PCH file can result in a complete, complete project recompile, slowing compilation. Apple has stopped using PCH files by default because the side effects of PCH even offset the optimization.

Clang Modules are used to replace PCH. For projects with this option enabled, we can replace #import with @import. For example: @import UIKit; This is equivalent to #import <UIKit/ uikit.h >. Aside from the small features of the auto-linking Framework, Clang Modules can be thought of as a modular PCH that has the advantage of a PCH that can cache header files while providing finer grained references.

Another trick is to use forward declarations. Use @class CLASSNAME instead of #import classname.h in.h files. By delaying the introduction of header files until needed, you can reduce the number of header files that users of your class need to introduce. The forward declaration also solves the problem of circular references to the two classes.

3 Cache compilation

Package common utility classes (Framework/.a) into a Framework or static library, so that when compiled, the code does not need to be recompiled. In the process of comparing the car dealer with the car e-estimation package, I found that the car dealer Realm, a third-party library, takes a long time to compile, while the car e-estimation does not have this cost, the car E-estimation did not use cocoapods to manage the Realm, but directly into the static library, so the time is greatly reduced. For stable third-party libraries, or tools we write, we can also use this approach to reduce packaging costs.

In fact, if we don’t clean before each packing, we will find that the packing time is actually much shorter. This is because Xcode itself uses incremental compilation, recompiling the modified files and references to the modified files on each compilation, but using previously cached compilation results for the unmodified parts. However, this incremental compilation is not stable, and I have previously had problems with incremental compilation not packaging the latest code during the packaging process.

Therefore, there are some third-party cache tools, and the easy access is CCache. CCache is a tool that can cache the compiled intermediates, and it is relatively stable, so it can improve the compilation speed. However, CCache does not support clang Modules, so you need to disable the Enable Modules in your project, and you also need to deal with the clang Module issue for third-party libraries managed by Cocoapods. The specific use method can refer to the article: Use CCache to make packing fly.

4 Modify project Settings

In general, our continuous integration tools are designed to be used by product managers or testers to experience features or verify bugs, and are not concerned with runtime performance unless they need to be on the App Store. With Release mode on mobile, however, various optimizations are turned on by default. These optimizations sacrifice compile performance for runtime speed, which is fine for the packages that are on the console, but not for the Daily Build packages.

Therefore, the idea of accelerated packaging and the idea of optimization are completely inverse, and what we need to do is turn off all possible optimizations.

The Valid Architectures option specifies the processor’s instruction set. For functional tests, we can specify that only the instruction set corresponding to the test machine is packaged.

Armv7 | armv7s | arm64 | arm64e is ARM processor’s instruction set i386 | x86_64 is Mac processor’s instruction set

IPhone XS, iPhone XS Max, iPhone XR 2017 A11 chip ARM64: IPhone 8, iPhone 8 Plus, iPhone X 2016 A10 chip ARM64: IPhone 6S Plus 2014 A8 chip arm64: iPhone 6, iPhone 6 Plus 2013 A7 chip ARM64: iPhone 5S armv7s IPhone5 | iPhone5C | iPad4(iPad with Retina Display) armV7: IPhone4 | iPhone4S | iPad | iPad2 | iPad3(The New iPad) | iPad mini | iPod Touch 3G | iPod Touch4

The i386 architecture is required for simulator 32-bit processor testing, and the X86_64 architecture is required for simulator 64-bit processor testing. The armV7 or ARMV7S architecture is required for real 32-bit processors, and the ARM64 architecture is required for real 64-bit processors.

Optimization Level Turns off compilation Optimization. The basic principle of optimization is to sacrifice compile-time performance for run-time performance. Common optimizations include removing useless code at compile time, retaining debugging information, inlining functions, and so on. So the secret to faster packaging is to do the opposite, sacrificing runtime performance for compile-time performance. For daily test packaging, you can change the Release state of the Optimize level to O0, which means no optimization.

Debug Information Format Does not generate dYSM files, and the Release value is changed to DWARF.

5 Adopt New Build System

Apple introduced the New Build System starting with Xcode 9 and used it as the default Build System in Xcode 10 instead of the Legacy Build System. Adopting a new build system can reduce build time.

Just to explain the rationale, with the old build system, when we built a program, we knew all the targets that needed to be built, the dependencies between those targets, and the order in which those targets were built. Using sequence will cause the waste of multiprocessor system resources, which is manifested as the waste of compilation time. The way to solve this problem is to use parallel compilation, which is also the core idea of the optimization of the new built system. Learn more about the New Build System and explore how Xcode New Build System can improve the Build speed.

6 Using a Script

Using script packaging is faster than using Xcode and saves intermediate interface time.

conclusion

The above is a summary of some methods to improve the speed of packaging, should be based on the actual situation of the project to choose the appropriate method of optimization. For the project changes are small and relatively easy to implement the main ways: reducing compiled files and resources, changing some third-party libraries to static libraries, engineering configuration modification.