Baidu APP iOS terminal package size 50M optimization practice (5) HEIC picture and useless class optimization practice

I. Introduction

The previous article introduced several methods of image optimization and code optimization. This article focuses on the optimization practice of HEIC image and useless class detection. HEIC is the abbreviation of High Efficiency Image Format (High Efficiency Image Format), which is a new image file format introduced by Apple in iOS 11 in 2017 to replace the JPEG image format to compress images more efficiently and reduce storage space occupied. HEIC supports multi-frame images, transparency, and 16-bit depth color, making it ideal for high-quality images and animations. This paper focuses on the feasibility of using HEIC images in Baidu APP and the package volume benefits, verifies the compatibility of HEIC images in Bundle and Asset Catalog, focuses on the mechanism of asset catalog management images, and records the special problems and problems found in the verification process. Solutions. The useless class introduces in detail how to reduce the code size by combining static analysis and dynamic analysis.

Review of a series of articles on Baidu APP iOS package volume optimization practice:

"Baidu APP iOS terminal package size 50M optimization practice (1) overview"

"Baidu APP iOS terminal package size 50M optimization practice (2) picture optimization"

"Baidu APP iOS terminal package size 50M optimization practice (3) resource optimization"

"Baidu APP iOS terminal package size 50M optimization practice (4) code optimization"

2. HEIC image format conversion and usage

2.1 Format conversion

There are three common HEIC image conversion methods: Mac image conversion function, Mac's own sips command, and ImageMagick command supported by multiple platforms.

2.1.1 Mac image conversion function:

  1. Right click on the picture, quick operation —> convert image

  2. Select HEIF as the format, and select the image size according to your needs

picture

2.1.2 sips tools:

sips is a command-line image processing tool that comes with MacOS. It has functions such as converting image format, modifying image size (expanding or resampling image), modifying quality, and setting copyright information.

Example: sips -s format heic -s formatOptions default [email protected] --out [email protected]

2.1.3 ImageMagick tools:

ImageMagick includes a command-line interface for performing complex image processing tasks, and an API for integrating its functionality into software applications. It is written in C and is available on various operating systems, including Linux, Windows, and macOS.

Usage reference:

https://imagemagick.org/index.php

Manual installation is required:

brew install imagemagick 

convert guid‍[email protected] guideview@3x.heic

2.2 HEIC is used in iOS

In the iOS system, ImageIO, Core Image, UIKit, and PhotoKit all support HEIC images. HEIC images can be placed in Bundle or Asset Catalog. Use the native method to create a UIImage object, which is consistent with the use of images such as JPEG and PNG.

// 加载本地图片
UIImage *image = [UIImage imageNamed:@"heifFileName"];
UIImage *image = [UIImage imageWithContentsOfFile:filePath];

// 由 网络请求的 NSData 解码
UIImage *image = [UIImage imageWithData:heifImageData];

2.3 HEIC image compatibility

encoding :

Hardcoded: A10 and above chip iOS devices (iPhone7)

decoding:

Hard solution: A9 and above chip iOS devices (iPhone6s), equipped with 6th generation and above Inter Core processing (Skylake).

Soft solution: iOS12 and macOS support soft decoding, (officially it is iOS11, the actual test iOS11 can not decode)

You can call ImageI/O related functions to obtain the supported image codec format. It is worth noting here that in iPhone6p, iOS11.0.4 does not support HEIC, that is, call CGImageSourceCopyTypeIdentifiers(); query decodable format contains public.heic, still It is unable to display HEIC pictures normally; HEIC pictures can be displayed normally on iPhone6p and iOS12.5.6 test machines.

//获取所支持的图片格式数组,解码
CFArrayRef decodeArr = CGImageSourceCopyTypeIdentifiers();
NSArray *decodeUTI = (__bridge NSArray *)decodeArr;
NSLog(@"解码支持%@", decodeUTI);

//获取所支持的图片格式数组,编码
CFArrayRef encodeArr = CGImageDestinationCopyTypeIdentifiers();
NSArray *encodeUTI = (__bridge NSArray *)encodeArr;
NSLog(@"编码支持%@", encodeUTI);

The minimum system version supported by Baidu APP is iOS10. iOS10 5s, iPhone6, and iPhone6p cannot directly decode HEIC images, and these three models will be affected. But this does not mean that these models cannot be compatible with HEIC pictures. The general idea is to introduce a third-party SDK, such as: SDWebImageHEIFCoder ( https://github.com/SDWebImage/SDWebImageHEIFCoder) , to increase decoding support, but the introduction of a third-party SDK increases the package size in a disguised form. In the test, it was found that putting the HEIC image into Asset Catalog management can display the image normally on the above three models.

3. Compatibility between Bundle and Asset Catalog

In the iOS system, image resources in the APP can be placed in Bundle and Asset Catalog. If the picture is placed in the Bundle, after the ipa package is installed on the device, the disk space occupied by the picture is the same as the actual size of the picture. However, the disadvantage of placing Bundle is that it is necessary to enlarge images of different sizes for different resolutions, which significantly increases the package size.

Apple recommends using Asset Catalog to manage built-in resources, including picture resources, audio and video, etc., and also supports HIEC pictures. The benefits of Asset Catalog are obvious. It supports app slicing, supports setting stretching areas, configures different pictures for different models, configures rendering colors, etc. Eventually all the files will be packaged into a .car compressed file.

For this, we selected two representative images, log.png is the image with alpha channel and [email protected] is the image without alpha channel. Then generate the corresponding HEIC image log.heic and guideview @3x.heic respectively, and the image has not undergone any other compression processing.

picture

[email protected]

picture

△log.png

3.1 Generate car file

From the log compiled by Xcode, it is found that the system uses its own actool tool to compress all .xcassets in the workspace to generate a .car file. Whether Xcode is connected to the test machine or not, the parameters for compiling Assets.car are different.

For an unconnected test machine, select Any iOS Device (arm64) to generate a general Assets.car file. The compilation parameters are as follows:

// Any iOS Device(arm64)
/Applications/Xcode.app/Contents/Developer/usr/bin/actool --output-format human-readable-text --notices --warnings --export-dependency-info /Users/xxxxx/Library/Developer/Xcode/DerivedData/ImageDemoS-auwsocxqgbwbgmfoiguzuahzizre/Build/Intermediates.noindex/ImageDemoS.build/Debug-iphoneos/ImageDemoS.build/assetcatalog_dependencies --output-partial-info-plist /Users/xxxxx/Library/Developer/Xcode/DerivedData/ImageDemoS-auwsocxqgbwbgmfoiguzuahzizre/Build/Intermediates.noindex/ImageDemoS.build/Debug-iphoneos/ImageDemoS.build/assetcatalog_generated_info.plist --app-icon AppIcon --accent-color AccentColor --compress-pngs --enable-on-demand-resources YES --development-region en --target-device iphone --target-device ipad --minimum-deployment-target 16.0 --platform iphoneos --compile /Users/xxxxx/Library/Developer/Xcode/DerivedData/ImageDemoS-auwsocxqgbwbgmfoiguzuahzizre/Build/Products/Debug-iphoneos/ImageDemoS.app /Users/xxxxxR/baidu/personal-code/ImageDemoS/ImageDemoS/Assets.xcassets /Users/xxxxx/baidu/personal-code/ImageDemoS/Media.xcassets

If the test machine is connected, the corresponding Assets.car file will be generated according to the model and system of the test machine. Key parameters --filter-for-thinning-device-configuration iPhone7,1 --filter-for-device-os-version 11.4.1, these two parameters can explain the compatibility of HEIC images on iOS11's iPhone6p. In the actual measurement, it was found that HEIC pictures placed in the Asset Catalog can actually be displayed on the iPhone 6p of iOS11, but at this time the pictures in the Asset Catalog are no longer HEIC coded. The specific compilation parameters are as follows:

// iPhone6p(iOS11.4.1)
/Applications/Xcode.app/Contents/Developer/usr/bin/actool --output-format human-readable-text --notices --warnings --export-dependency-info /Users/xxxxx/Library/Developer/Xcode/DerivedData/ImageDemoS-auwsocxqgbwbgmfoiguzuahzizre/Build/Intermediates.noindex/ImageDemoS.build/Debug-iphoneos/ImageDemoS.build/assetcatalog_dependencies --output-partial-info-plist /Users/xxxxx/Library/Developer/Xcode/DerivedData/ImageDemoS-auwsocxqgbwbgmfoiguzuahzizre/Build/Intermediates.noindex/ImageDemoS.build/Debug-iphoneos/ImageDemoS.build/assetcatalog_generated_info.plist --app-icon AppIcon --accent-color AccentColor --compress-pngs --enable-on-demand-resources YES --optimization space --filter-for-thinning-device-configuration iPhone7,1 --filter-for-device-os-version 11.4.1 --development-region en --target-device iphone --target-device ipad --minimum-deployment-target 9.0 --platform iphoneos --compile /Users/xxxxx/Library/Developer/Xcode/DerivedData/ImageDemoS-auwsocxqgbwbgmfoiguzuahzizre/Build/Products/Debug-iphoneos/ImageDemoS.app /Users/xxxxx/baidu/personal-code/ImageDemoS/ImageDemoS/Assets.xcassets /Users/xxxxx/baidu/personal-code/ImageDemoS/Media.xcassets

At the same time, we found that when the actool tool is used to process .xcassets, the following warning will appear instead of error. From the warning message given by actool, HIEC images are only supported on systems after iOS11, but when generating the Asset.car file, the actool tool will generate compatible images for models below iOS11 according to the specified minimum system version. Although the size of the image may be larger, the HIEC image is compatible with all models in the Asset Catalog. However, HIEC images placed in Bundle are not compatible with systems below iOS11. Further proof of atool's own handling of HEIC image compatibility.

/* com.apple.actool.document.warnings */
/Media.xcassets:./logHEICAlpha.imageset/[universal][][][3x][][][][][][][][][][]: warning: You're targeting iOS 9.0, but HEIF files can only be accessed from an Asset Catalog in iOS 11.0 and later.

3.2 Parse the car file

To parse the Assets.car file, you can use the Mac tool assetutil, which can remove unnecessary pictures in the general Assets.car, and can also parse the detailed content of Assets.car. You can also use Asset Catalog Tinkerer to display pictures, refer to: https://github.com/insidegui/AssetCatalogTinkerer. Here we use assetutil to parse the contents of Assets.car. The command is as follows:

assetutil -I Assets.car > Assets.json

In the following table, to analyze the file information in the general Assets.car, you first need to understand the meaning of the following fields:

SizeOnDisk:这是图片在Assets.car里实际的体积
Encoding:编码方式,HEIF就是HEIC图片的编码方式
Compression:压缩算法

We can draw the following conclusions:

1. The size of the PNG image to HEIC image will decrease;

2. After the PNG image and HEIC image are processed by actool, the image size in the car file is inconsistent with the actual size;

3. The size of the image in the car file is related to the size of the package, the encoding method, and the compression algorithm. The final size of the PNG and HEIC images is subject to the data in the SizeOnDisk field;

4. actool will make pictures compatible with iOS system and devices, and convert HEIC pictures to other displayable formats on devices that do not support HEIC.

Image size: 15,444 (log.png) 10,867 (log.heic)

Image size: 72,547 ([email protected]) 33,589 ([email protected])

4. Alpha channel compatibility issues

During the actual operation, we found that after some PNG images with alpha channel are converted to HEIC images, various problems such as the image cannot be displayed, white, green, etc. will appear on iOS11, iOS12, and iOS13 systems, but there are also some The picture with alpha channel is displayed exactly correct. We made a series of explorations for such problems and finally determined the cause of the problem. All images with alpha channel compressed by pngquant: https://pngquant.org lossy (60-90) will have the above problems after converting to HEIC images. The following is the specific analysis process and related data.

picture

△The picture is displayed in white

picture

△Transparent display is green

4.1 Problem Analysis Ideas

There are a few issues to consider first:

1. Why does iOS14 and iOS15 display the same HIEC image normally, but iOS11, iOS12, and iOS13 have problems?

2. Why are some HEIC images with alpha channel also have problems on iOS11, iOS12, and iOS13 systems, while some images can be displayed normally on all systems?

Step 1: Determine the image encoding and decoding data, convert the PNG and problematic HEIC images to Bitmap, and check the RGBA value

The above problem only occurs on some HEIC pictures with an alpha channel. First, analyze the color distortion problem of alpha channel HIEC pictures from the perspectives of encoding and decoding. All pictures on the iOS device will first be decoded to generate a Bitmap bitmap, and then rendered into a picture, so it is necessary to obtain the Bitmap data and picture information of a picture. A very critical structure for obtaining Bitmap data is CGImageRef. There are three common ways to obtain CGImageRef:

  1. UIKit provides the CGImage property of UIImage, which is the most commonly used method;

  2. The CGImageSourceCreateImageAtIndex function provided by ImageI/O is suitable for parsing images from files;

  3. CGBitmapContextCreateImage provided by Core graphics, which is suitable for the known bitmap graphics context;

Here get CGImageRef directly from UIImage.

/// 获取图片信息和像素
/// - Parameters:
///   - image: <#image description#>
 -(void)dumpImageInfo:(UIImage *)image
{
    // 获取CGImageRef
    CGImageRef cgimage = image.CGImage;

    size_t width  = CGImageGetWidth(cgimage);
    size_t height = CGImageGetHeight(cgimage);
    size_t bpr = CGImageGetBytesPerRow(cgimage);
    size_t bpp = CGImageGetBitsPerPixel(cgimage);
    size_t bpc = CGImageGetBitsPerComponent(cgimage);
    size_t bytes_per_pixel = bpp / bpc;

    CGBitmapInfo info = CGImageGetBitmapInfo(cgimage);
    NSLog(
        @"\n"
//        "===== %@ =====\n"
        "CGImageGetHeight: %d\n"
        "CGImageGetWidth:  %d\n"
        "CGImageGetColorSpace: %@\n"
        "CGImageGetBitsPerPixel:     %d\n"
        "CGImageGetBitsPerComponent: %d\n"
        "CGImageGetBytesPerRow:      %d\n"
        "CGImageGetBitmapInfo: 0x%.8X\n"
        "  kCGBitmapAlphaInfoMask     = %s\n"
        "  kCGBitmapFloatComponents   = %s\n"
        "  kCGBitmapByteOrderMask     = %s\n"
        "  kCGBitmapByteOrderDefault  = %s\n"
        "  kCGBitmapByteOrder16Little = %s\n"
        "  kCGBitmapByteOrder32Little = %s\n"
        "  kCGBitmapByteOrder16Big    = %s\n"
        "  kCGBitmapByteOrder32Big    = %s\n",
//        file,
        (int)width,
        (int)height,
        CGImageGetColorSpace(cgimage),
        (int)bpp,
        (int)bpc,
        (int)bpr,
        (unsigned)info,
        (info & kCGBitmapAlphaInfoMask)     ? "YES" : "NO",
        (info & kCGBitmapFloatComponents)   ? "YES" : "NO",
        (info & kCGBitmapByteOrderMask)     ? "YES" : "NO",
        (info & kCGBitmapByteOrderDefault)  ? "YES" : "NO",
        (info & kCGBitmapByteOrder16Little) ? "YES" : "NO",
        (info & kCGBitmapByteOrder32Little) ? "YES" : "NO",
        (info & kCGBitmapByteOrder16Big)    ? "YES" : "NO",
        (info & kCGBitmapByteOrder32Big)    ? "YES" : "NO"
    );

    // 获取位图数据
    CGDataProviderRef provider = CGImageGetDataProvider(cgimage);
    NSData* data = (__bridge NSData *)CGDataProviderCopyData(provider);
//    [data autorelease];
    const uint8_t* bytes = [data bytes];

    printf("Pixel Data:\n");
    for(size_t row = 0; row < height; row++)
    {
        for(size_t col = 0; col < width; col++)
        {
            const uint8_t* pixel =
                &bytes[row * bpr + col * bytes_per_pixel];

            printf("(");
            for(size_t x = 0; x < bytes_per_pixel; x++)
            {
                printf("%.2d", pixel[x]);
                if( x < bytes_per_pixel - 1 )
                    printf(",");
            }

            printf(")");
            if( col < width - 1 )
                printf(", ");
        }

        printf("\n");
    }
}

For the problematic HEIC image, analyze the Bitmap value and you can find that for the Bitmap arranged in RGBA, the white transparency should be (0, 0, 0, 0). The above is (71,112,77,112), and it is (71,112,77,00) on iOS15 and iOS16. It is obvious that the reason why the HEIC image transferred by the sips tool remains colorless and transparent on iOS15 and iOS16 systems is that the alpha channel value is 0, however its actual color is green. After experiments, it is known that [UIImage imageNamed:] of iOS12, 13, and 14 systems will have a slight error in parsing the Bitmap for pictures with Alpha, resulting in the Alpha value not being 0 on iOS12, 13, and 14 systems, so it appears green.

picture

Step 2: Determine the impact of transcoding tools and compression tools on the image

The first step can basically be determined to be a problem with the picture itself. The pictures in the Baidu APP are compressed by a compression tool before they are integrated into the app. It may be that the original PNG picture has been processed to cause the above problem. Use the test machine (iPhone7p iOS12) that supports the hard-coded iOS12 system, call the function CGImageSourceCreateImageAtIndex provided by Image I/O to first decode the PNG image, and then call CGImageDestinationCreateWithData to re-encode the HEIC image. Converted code:

/// 从支持解码的图片创建CGImageRef
/// - Parameter path: 图片路径
CGImageRef createCGImageFromFile (NSString* path)
{
    // Get the URL for the pathname passed to the function.
    NSURL *url = [NSURL fileURLWithPath:path];
    CGImageRef        myImage = NULL;
    CGImageSourceRef  myImageSource;
    CFDictionaryRef   myOptions = NULL;
    CFStringRef       myKeys[2];
    CFTypeRef         myValues[2];

    // Set up options if you want them. The options here are for
    // caching the image in a decoded form and for using floating-point
    // values if the image format supports them.
    myKeys[0] = kCGImageSourceShouldCache;
    myValues[0] = (CFTypeRef)kCFBooleanTrue;
    myKeys[1] = kCGImageSourceShouldAllowFloat;
    myValues[1] = (CFTypeRef)kCFBooleanTrue;
    // Create the dictionary
    myOptions = CFDictionaryCreate(NULL, (const void **) myKeys,
                   (const void **) myValues, 2,
                   &kCFTypeDictionaryKeyCallBacks,
                   & kCFTypeDictionaryValueCallBacks);
    // Create an image source from the URL.
    myImageSource = CGImageSourceCreateWithURL((CFURLRef)url, myOptions);
    CFRelease(myOptions);
    // Make sure the image source exists before continuing
    if (myImageSource == NULL){
        fprintf(stderr, "Image source is NULL.");
        return  NULL;
    }
    // Create an image from the first item in the image source.
    myImage = CGImageSourceCreateImageAtIndex(myImageSource, 0, NULL);

    CFRelease(myImageSource);
    // Make sure the image exists before continuing
    if (myImage == NULL){
         fprintf(stderr, "Image not created from image source.");
         return NULL;
    }

    return myImage;
}

/// 将任意一种格式的图片由UIImage编码为HEIC图片存储
/// - Parameters:
///   - image: <#image description#>
///   - path: <#path description#>
- (void)generateNewHEIC:(UIImage *)image savePath:(NSString *)path{
    
    NSMutableData *imageData = [NSMutableData data];
    // HEIC图片编码格式
    CFStringRef imageUTType =  CFSTR("public.heic");
    CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)imageData, imageUTType, 1, NULL);
    if (!destination) {
        // 无法编码,基本上是因为目标格式不支持
        NSLog(@"无法编码");
        return;
    }
    CGImageRef imageRef = image.CGImage; // 待编码的CGImage
    // 可选元信息,比如EXIF方向
    CGImagePropertyOrientation exifOrientation = kCGImagePropertyOrientationDown;
    NSMutableDictionary *frameProperties = [NSMutableDictionary dictionary];
//    imageProperties[(__bridge_transfer NSString *) kCGImagePropertyExifDictionary] = @(exifOrientation);
    // 添加图像和元信息
    CGImageDestinationAddImage(destination, imageRef, (__bridge CFDictionaryRef)frameProperties);
    if (CGImageDestinationFinalize(destination) == NO) {
        // 编码失败
        imageData = nil;
    }
    // 编码成功,清理……
    CFRelease(destination);
    // 保存新生成的HEIC图片
    if(imageData) {
        NSURL *url = [NSURL fileURLWithPath:path];
        [imageData writeToURL:url atomically:YES];
    }
}

iPhone7p uses ImageI/O to convert PNG to HEIC, and the encoding is (00,00,01,01). For the HEIC image converted by the test machine itself, call [UIImage imageNamed:] to obtain the UIImage object, and the displayed image has no alpha channel. Green edge problem. After the above operations, it is basically located that the problem of the green edge of the picture with the alpha channel occurs in the process of sips converting the PNG picture to the HEIC picture, and since this problem only occurs on some PNG pictures, it can be concluded that it is the original PNG The image has been processed by other compression algorithms, resulting in problems when converting sips to HEIC images. Finally, after investigation, it is found that the PNG image with Alpha channel that has been lossy compressed by pngquant cannot be converted into HEIC image correctly.

5. Image Best Practices

The iOS package volume is mainly composed of code and resources. In the practice of package volume optimization, it is found that compared with code, resource benefits are easier to implement. Commonly used resource optimization methods in Baidu APP include: PMS delivery, ZIP compression, image compression, and format conversion. In order to prevent the problem of adding large resources and large pictures in the future, when RD submits code, optimize the git hook function:

1. Modify the interception threshold, and reduce the interception threshold for large resources and large images from 50KB to 20KB;

2. Added picture optimization reminder function, automatically compresses and converts pictures when submitting new pictures, and gives suggestions for the best size of pictures.

5.1 Scheme

1. When executing git commit, check the submitted file, if it is a non-code file, check the file size. The access threshold for large resources and large images has been reduced from 50KB to 20KB;

2. Calculate the size of various optimized images in the Bundle and Asset Catalog, and calculate the best optimization method. The text prompts RD optimization, and will not intercept the submission

3. There are two types of image optimization methods, one is the storage location, put Bundle and Asset Catalog; the other is to process the image, there are two processing methods of compression and format conversion, and the two are combined to get the best method :

  • Put Bundle: Not recommended, the size of the image in the installation package is the size of the image itself, Xcode will not process it, and it cannot be compatible with HEIC images in systems below iOS11;

  • Put Asset Catalog: Recommended, Xcode will use atool tool to process images when compiling and compiling, optimize image size, and be compatible with HEIC images;

  • PNGquant compression: lossy compression of PNG images, using the image compression parameters before Baidu APP, which is the original logic of git hook;

  • MozJPEG compression: Lossy compression of JPG images, new compression tools, https://calendar.perfplanet.com/2014/mozjpeg-3-0/;

  • HEIC image: Lossless conversion, Baidu APP can only be used in Asset Catalog, need to return to iOS11 system to see if it is displayed normally;

Note: Since the picture is placed in the Asset Catalog, Xcode will use the atool tool to process the picture when it is packaged and compiled, so the size of the picture in the installation package is not equal to the size of the picture itself. The script will compile the image into an Asset.car file, and read the actual size of the image in the installation package.

picture

△Picture submission test

6. Detect useless classes

6.1 Analysis of Useless Class Detection Principles

Baidu APP is a very large project. Each version will have many new requirements. However, with personnel changes, operational activities and version iterations, some functions have no entry, and some codes will no longer be referenced. For example, it has been solidified. AB experiment code, cloud control switch code, etc., during the code reconstruction process, there will be redundant code that is forgotten to be deleted, and the code is still left in the project after the event is offline. Useless code detection is a good solution to optimize the package size from the perspective of code. Code optimization includes useless classes, useless methods, repeated codes, and operational codes. The following section focuses on the detection and optimization of useless classes. The difficulty of useless class detection is that OC is a dynamic language, and the probability of false positives during detection will be high, which will cause a waste of manpower for RD. The general idea of ​​useless class detection is divided into two parts, the combination of static detection and dynamic detection.

Static detection is to analyze the code reference relationship and structure from the perspective of compiled products. Analyze the Linkmap file and Mach-O file, and find out the unused and referenced Class and method according to the data in the Segment. However, this method has great limitations. For example, some can be affected by operations, and how to execute them is determined by the server-side cloud control; some classes that are initially tested through the Runtime cannot be recognized.

Dynamic analysis is to analyze whether the code is initialized from the perspective of code running. During the running of the APP, the classes initialized in the life cycle of the APP are recorded, and the related classes involved in the functions used by the user will be recorded. Otherwise, some functions are not. If used, the corresponding classes will not be recorded. Dynamic detection is to exhaustively enumerate all the classes used in the use of APP functions.

picture

△ Useless class analysis and distribution

Seven, static analysis

Static analysis requires Linkmap files and Mach-O files. Linkmap files record the correspondence between all Symbol addresses, Symbols, and Symbol Sizes, and Mach-O files record class structures and addresses. Combining the Linkmap file and Mach-O can restore all the information of each Class.

The __DATA __objc_classrefs section in the Mach-o file records the address of the referenced class, and the __DATA __objc_classlist section records the addresses of all classes. The difference set can be used to obtain the address of the unused class, and then symbolized to obtain the unreferenced class. class information.

7.1 Analyzing Mach-O

You can print segment information in Mach-o through the tool otool that comes with Mac.

% file -b BaiduBoxApp.app/BaiduBoxApp #获取Mach-O架构
Mach-O 64-bit executable arm64

% otool -arch arm64 -oV BaiduBoxApp.app/BaiduBoxApp > ovrelease.txt #解析Mach-O内容

The output content mainly includes the following parts, among which __DATA, __objc_classlist is the complete set of classes, __DATA, __objc_classrefs are the referenced classes. If it is a Debug package, you can directly obtain the class name, but the release package generally only has symbol addresses. Using these addresses, you can restore the symbols in the corresponding and Linkmap files, that is, you can get the specific class name.

'Contents of (__DATA,__objc_classlist) section',  # classlist节标识
'Contents of (__DATA,__objc_classrefs) section',  # classrefs节标
'Contents of (__DATA,__objc_superrefs) section',  # 父类节标
'Contents of (__DATA,__objc_catlist) section',  # category节标
'Contents of (__DATA,__objc_protolist) section',
'Contents of (__DATA,__objc_selrefs) section',
'Contents of (__DATA,__objc_imageinfo) section'

picture

△debug package

picture

△release package

7.2 Notes

1. In the process of actual analysis, it is found that if the subclass of a class is instantiated and the parent class is not instantiated, the parent class will not appear in the __objc_classrefs section at this time, and it needs to be added to the unused class Part of the parent class is filtered out.

2. The same method name may exist in multiple classes. Because the two code segments __cstring and __objc_methname in the MachO file record the hexadecimal representation of the ASCII code of the method name character. If multiple classes have the same method name, the same method name will enter the Dead Stripped Symbols of the link map, and only one will be left at the end.

3. If segment migration is done, the otool tool may not be able to resolve the corresponding method name, but we can restore the specific symbol in the linkmap through the symbol address.

picture

△Symbol Analysis

Eight, dynamic analysis

8.1 Principle of dynamic analysis

In the class structure of OC, there is an isa pointer pointing to the meta-class of the corresponding class. Through the analysis of the structure of the meta-class, we can find that there is a flag flag in the class_rw_t of the meta-class. Through the calculation of the flag flag, we can know whether the current class has been initialized at runtime.

// class is initialized
#define RW_INITIALIZED        (1<<29)

struct objc_class : objc_object {

    bool isInitialized() {
    return getMeta()->data()->flags & RW_INITIALIZED;
    }
};

In the objc-runtime source code extracted above, the function to obtain whether the current class has been initialized under the objc_class structure. But in the application, we cannot directly call the functions in the class structure, so in the Baidu APP project, customize the same structure as the system class, and implement the corresponding isInitialized() function. Through assignment conversion, we can get the data in the meta-class corresponding to the specified class, that is, we can judge whether the specified class has been initialized (used) in the current life cycle.

8.2 Technical realization

#define RW_INITIALIZED        (1<<29)

# if __arm64__
#   define ISA_MASK        0x0000000ffffffff8ULL
# elif __x86_64__
#   define ISA_MASK        0x00007ffffffffff8ULL
# endif

struct lazyFake_objc_class : lazyFake_objc_object {
    //提供metaClass函数,获取元类对象
    lazyFake_objc_class* metaClass() {
        #if __LP64__
        //isa指针需要经过一次 &ISA_MASK操作之后才得到真正的地址
            return (lazyFake_objc_class *)((long long)isa & ISA_MASK);
        #else
            return (lazyFake_objc_class *)((long long)isa);
        #endif
    }
    bool isInitialized() {
        return metaClass()->data()->flags & RW_INITIALIZED;
    }
};

2. First obtain all custom OC classes in the Baidu APP project

Dl_info info;
dladdr(&_mh_execute_header, &info);
classes = objc_copyClassNamesForImage(info.dli_fname, &classCount);

3. Traverse the custom classes, and assign values ​​to them one by one to convert them into custom structures, and use the custom class structure method to obtain whether the current class has been initialized.

struct lazyFake_objc_class *objectClass = (__bridge struct lazyFake_objc_class *)cls;

BOOL isInitial = objectClass->isInitialized();

9. Summary

1. Compared with PNG, HEIC images can reduce the image size for some images, and the income ranges from 10% to 70%. Specific problems are analyzed in detail, and git hook check scripts are written to provide guidance;

2. HEIC images placed in the Asset Catalog are compatible with all models and systems above iOS10;

3. HEIC pictures can only be decoded on the iOS12 system when put in Bundle, which is contrary to the conclusion given by Apple. If the minimum supported system of the APP is less than iOS12, HEIC images are prohibited from being bundled. Models with chips above A9 have a hard solution, which is faster;

4. If the PNG image with alpha channel has not been compressed by pngquant lossy, it can be displayed normally by using the sips command to convert the HEIC image directly;

5. PNG images with alpha channels that have been lossy compressed by pngquant will display a green screen on iOS12, 13, and 14 systems, and iOS115 and iOS16 will display normally. Although the display is normal, the color decoding of the RGB bitmap is wrong, just because the alpha is 0, the green becomes transparent;

6. Regardless of whether it is a PNG or HEIC image, under the management of the Asset Catalog, the volume generated by packaging is different from the original image, and will undergo different processing and compression, which may become larger or smaller, and the final product shall prevail;

7. pngquant is suitable for compressing the PNG in the Bundle to obtain income, and it should not be processed for the pictures in the Asset Catalog, because this income is actually obtained through lossy compression, and it will cause the compressed PNG with Alpha channel to be unable to convert to HEIC;

8. The useless class detection combines dynamic detection and static detection. The detection is stricter, mainly to reduce the false alarm rate and reduce the impact on RD. During the actual operation, it is found that some useless classes will be missed. Accuracy and coverage need to be adjusted dynamically based on demand.

—— END——

References :

[1]、503_WWDC 2017 CMF_03_D:

https://devstreaming-cdn.apple.com/videos/wwdc/2017/503i6plfvfi7o3222/503/503_introducing_heif_and_hevc.pdf

[2], iOS code slimming practice: delete useless classes:

httpshttps://juejin.cn/post/6844903922201526285

Recommended reading :

Baidu Knows Cloud and Architecture Evolution

Baidu APP iOS terminal package size 50M optimization practice (4) code optimization

Baidu App Startup Performance Optimization Practice

Application practice of light sweeping motion effect on mobile terminal

Android SDK security hardening issues and analysis

Large-scale quantitative practice of search semantic model

Clarification about MyBatis-Flex plagiarizing MyBatis-Plus Arc browser officially released 1.0, claiming to be a substitute for Chrome OpenAI officially launched Android version ChatGPT VS Code optimized name obfuscation compression, reduced built-in JS by 20%! LK-99: The first room temperature and pressure superconductor? Musk "purchased for zero yuan" and robbed the @x Twitter account. The Python Steering Committee plans to accept the PEP 703 proposal, making the global interpreter lock optional . The number of visits to the system's open source and free packet capture software Stack Overflow has dropped significantly, and Musk said it has been replaced by LLM
{{o.name}}
{{m.name}}

Guess you like

Origin my.oschina.net/u/4939618/blog/10092195
Recommended