zoukankan      html  css  js  c++  java
  • iOS包大小计算

    一、LinkMap文件分析

          说明:LinkMap数据是根据文章《LinkMap文件分析》中方法实验实测数据。

    如何获得LinkMap文件
    1.在XCode中开启编译选项Write Link Map File 
    
    XCode -> Project -> Build Settings -> 把Write Link Map File选项设为yes,并指定好linkMap的存储位置
    2.工程编译完成后,在编译目录里找到Link Map文件(txt类型)
    在Build Settings里Path to Link Map File对应Link Map的路径,以下是默认路径: /Users/xxx/Library/Developer/Xcode/DerivedData/YourApp/Build/Intermediates.noindex/YourApp.build/Debug-iphoneos/YourApp.build/YourApp-LinkMap-normal-arm64.txt

    LinkMap里有了每个目标文件每个方法每个数据的占用大小数据,所以只要写个脚本,就可以统计出每个.o最后的大小,属于一个.a静态链接库的.o加起来,就是这个库在APP里占用的空间大小。

    LinkMap包含以下部分:

    # Path: .../*.app文件
    # Arch: arm64
    # Object files: .../*.o文件
    # Sections: Text和Data分类大小
    # Symbols: 与Object files对应起来,是Object files的详细Symbal信息,包括地址和大小
    # Dead Stripped Symbols:
    // linkmap.js
    var readline = require('readline'),
        fs = require('fs');
    
    var LinkMap = function(filePath) {
        this.files = []
        this.filePath = filePath
    }
    
    LinkMap.prototype = {
        start: function(cb) {
            var self = this
            var rl = readline.createInterface({
                input: fs.createReadStream(self.filePath),
                output: process.stdout,
                terminal: false
            });
            var currParser = "";
            rl.on('line', function(line) {
                if (line[0] == '#') {
                    if (line.indexOf('Object files') > -1) {
                        currParser = "_parseFiles";
                    } else if (line.indexOf('Sections') > -1) {
                        currParser = "_parseSection";
                    } else if (line.indexOf('Symbols') > -1) {
                        currParser = "_parseSymbols";
                    }
                    return;
                }
                if (self[currParser]) {
                    self[currParser](line)
                }
            });
    
            rl.on('close', function(line) {
                cb(self)
            });
        },
    
        _parseFiles: function(line) {
            var arr =line.split(']')
            if (arr.length > 1) {
                var idx = Number(arr[0].replace('[',''));
                var file = arr[1].split('/').pop().trim()
                this.files[idx] = {
                    name: file,
                    size: 0
                }
            }
        },
    
        _parseSection: function(line) {
        },
    
        _parseSymbols: function(line) {
            var arr = line.split('	')
            if (arr.length > 2) {
                var size = parseInt(arr[1], 16)
                var idx = Number(arr[2].split(']')[0].replace('[', ''))
                if (idx && this.files[idx]) {
                    this.files[idx].size += size;
                }
            }
        },
    
        _formatSize: function(size) {
            if (size > 1024 * 1024) return (size/(1024*1024)).toFixed(2) + "MB"
            if (size > 1024) return (size/1024).toFixed(2) + "KB"
            return size + "B"
        },
    
        statLibs: function(h) {
            var libs = {}
            var files = this.files;
            var self = this;
            for (var i in files) {
                var file = files[i]
                var libName
                if (file.name.indexOf('.o)') > -1) {
                    libName = file.name.split('(')[0]
                } else {
                    libName = file.name
                }
                if (!libs[libName]) {
                    libs[libName] = 0
                }
                libs[libName] += file.size
            }
            var i = 0, sortLibs = []
            for (var name in libs) {
                sortLibs[i++] = {
                    name: name,
                    size: libs[name]
                }
            }
            sortLibs.sort(function(a,b) {
                return a.size > b.size ? -1: 1
            })
            if (h) {
                sortLibs.map(function(o) {
                    o.size = self._formatSize(o.size)
                })
            }
            return sortLibs
        },
    
        statFiles: function(h) {
            var self = this
            self.files.sort(function(a,b) {
                return a.size > b.size ? -1: 1
            })
            if (h) {
                self.files.map(function(o) {
                    o.size = self._formatSize(o.size)
                })
            }
            return this.files
        }
    }
    
    if (!process.argv[2]) {
        console.log('usage: node linkmap.js filepath -hl')
        console.log('-h: format size')
        console.log('-l: stat libs')
        return
    }
    var isStatLib, isFomatSize
    var opts = process.argv[3];
    if (opts && opts[0] == '-') {
        if (opts.indexOf('h') > -1) isFomatSize = true
        if (opts.indexOf('l') > -1) isStatLib = true
    }
    
    var linkmap = new LinkMap(process.argv[2])
    linkmap.start(function(){
        var ret = isStatLib ? linkmap.statLibs(isFomatSize) 
                            : linkmap.statFiles(isFomatSize)
        for (var i in ret) {
            console.log(ret[i].name + '	' + ret[i].size)
        }
    })

    执行:

    node linkmap.js LinkMap-normal-arm64.txt -hl

    得到:

    YourSDK(YourSDK-arm64-master.o)    122.76KB

    二、xcodebuild总包大小分析

    分析:Release 包(arm64)为什么比debug包(x86_64)大那么多?

    分析步骤

    1.  抽出YourSDK的arm64看包大小(1.6M)

    lipo  -thin arm64 -output YourSDK_arm64.a YourSDK.framework/YourSDKls -al YourSDK_arm64.a
    -rw-r--r--  1 yushu.lxy  staff  1606208 11 18 15:58 YourSDK_arm64.a

    2.  整体

    size YourSDK_armv64.a
    __TEXT    __DATA    __OBJC    others    dec    hex
    86591    39492    0    1190617    1316700    14175c    YourSDK_arm64.a(YourSDK-arm64-master.o)
    30    240    0    4566    4836    12e4    YourSDK_arm64.a(libPods-YourSDK.a-arm64-master.o)

    查看.a文件下.o文件大小

    ar -t -v YourSDK_arm64.a
    rw-r--r--     501/20         5776 Nov 18 15:58 2019 __.SYMDEF SORTED
    rw-r--r--       0/0       1593168 Jan  1 08:00 1970 YourSDK-arm64-master.o
    rw-r--r--       0/0          6976 Jan  1 08:00 1970 libPods-YourSDK.a-arm64-master.o

    3.  详细

    size -m YourSDK_arm64.a
    YourSDK_arm64.a(YourSDK-arm64-master.o):
    Segment : 1316713
        Section (__TEXT, __text): 68676(比debug的包小)
        Section (__TEXT, __objc_methname): 8393
        Section (__TEXT, __cstring): 5091
        Section (__TEXT, __objc_classname): 1636
        Section (__TEXT, __objc_methtype): 1937
    Section (__TEXT, __literal16): 128(只有debug包有)
        Section (__TEXT, __literal4): 8(只有debug包有)
        Section (__TEXT, __literal8): 144(比debug的包小)
    Section (__TEXT, __eh_frame): 16480
        Section (__TEXT, __gcc_except_tab): 608
        Section (__TEXT, __ustring): 42
        Section (__TEXT, __const): 64
        Section (__DATA, __const): 1560
        Section (__DATA, __cfstring): 4736
        Section (__DATA, __objc_classlist): 400
        Section (__DATA, __objc_catlist): 8
        Section (__DATA, __objc_protolist): 56
        Section (__DATA, __objc_imageinfo): 8
        Section (__DATA, __objc_const): 24224
        Section (__DATA, __objc_selrefs): 2752
        Section (__DATA, __objc_protorefs): 8
        Section (__DATA, __objc_classrefs): 592
        Section (__DATA, __objc_superrefs): 192
        Section (__DATA, __objc_ivar): 252(比debug的包小)
        Section (__DATA, __objc_data): 4000
        Section (__DATA, __data): 672
        Section (__DATA, __bss): 32
        Section (__LD, __compact_unwind): 11424
        Section (__LLVM, __bundle): 1179193(+)
        total 1316700
    total 1316713
    YourSDK_arm64.a(libPods-YourSDK.a-arm64-master.o):
    Segment : 4838
        Section (__TEXT, __text): 0
        Section (__TEXT, __objc_classname): 30
        Section (__DATA, __objc_classlist): 8
        Section (__DATA, __objc_imageinfo): 8
        Section (__DATA, __objc_const): 144
        Section (__DATA, __objc_data): 80
        Section (__LLVM, __bitcode): 4480 (+)
        Section (__LLVM, __cmdline): 86 (+)
        total 4836
    total 4838

    YourSDK SDK实际大小应该是:1316713-1179193 = 137KB(与LinkMap分析接近)

    分析:

    上面段数据LLVM bundle部分是release包比debug包大的原因,是因为release包包含了bitCode数据

    参考《关于bitcode, 知道这些就够了

    (1)bitcode是源码和机器码之间的中间形式。

    (2)为什么苹果要强推bitcode?开发者把bitcode提交到App Store Connect之后,如果苹果发布了使用新芯片的iPhone,支持更高效的指令,开发者不需要做任何操作,App Store Connect自己就可以编译出针对新产品优化过的app并通过App Store分发给用户,不需要开发者自己重新打包上架。

    (3)苹果可以将bitcode代码进行一些逻辑等价的转换,使得代码的执行效率更高,体积更小。

    实验:APP的project开启bitcode和关闭bitcode,查看手机中app的实际大小,开启bitcode的包更小。

    size -m YourSDK_arm64.a
    YourSDK_arm64.a(YourSDK-arm64-master.o):
    Segment : 943490
        Section (__TEXT, __text): 53184
        Section (__TEXT, __objc_classname): 633
        Section (__TEXT, __objc_methname): 7919
        Section (__TEXT, __objc_methtype): 850
        Section (__TEXT, __cstring): 7352
        Section (__TEXT, __literal8): 64
        Section (__TEXT, __gcc_except_tab): 1724
        Section (__TEXT, __const): 112
        Section (__DATA, __const): 1864
        Section (__DATA, __cfstring): 4000
        Section (__DATA, __objc_classlist): 200
        Section (__DATA, __objc_protolist): 8
        Section (__DATA, __objc_imageinfo): 8
        Section (__DATA, __objc_const): 21760
        Section (__DATA, __objc_selrefs): 1776
        Section (__DATA, __objc_classrefs): 352
        Section (__DATA, __objc_superrefs): 24
        Section (__DATA, __objc_ivar): 620
        Section (__DATA, __objc_data): 2000
        Section (__DATA, __data): 96
        Section (__DATA, __bss): 40
        Section (__DATA, __common): 8
        Section (__LD, __compact_unwind): 15968
        Section (__LLVM, __bundle): 822914
        total 943476
    total 943490

    分析结论

    (1) LinkMap分析的大小不包含打入framework中的bundle信息,建议用LinkMap分析的包大小至少要加上bundle的大小。

    (2)经多次安装实验,真实安装包后, APP大小增长是比LinkMap给出的包大小大的。

    (3)给外部的包大小信息,暂时跟大家保持一致“使用LinkMap给出的数据”。

  • 相关阅读:
    正经学C#_循环[do while,while,for]:[c#入门经典]
    Vs 控件错位 右侧资源管理器文件夹点击也不管用,显示异常
    asp.net core 获取当前请求的url
    在实体对象中访问导航属性里的属性值出现异常“There is already an open DataReader associated with this Command which must be
    用orchard core和asp.net core 3.0 快速搭建博客,解决iis 部署https无法登录后台问题
    System.Data.Entity.Core.EntityCommandExecution The data reader is incompatible with the specified
    初探Java设计模式3:行为型模式(策略,观察者等)
    MySQL教程77-CROSS JOIN 交叉连接
    MySQL教程76-HAVING 过滤分组
    MySQL教程75-使用GROUP BY分组查询
  • 原文地址:https://www.cnblogs.com/Xylophone/p/14156729.html
Copyright © 2011-2022 走看看