如何精简iOS SDK文件大小

栏目: Objective-C · 发布时间: 7年前

内容简介:我们的SDK项目随着各种功能的加入,SDK文件也越来越大。Objective-C的库最终会把用到的,没有用到类和方法都连接进App里,所以精简SDK大小很有必要,有助于减少最终App的size。库文件主要分动态库和静态库两种。动态库:

我们的SDK项目随着各种功能的加入,SDK文件也越来越大。Objective-C的库最终会把用到的,没有用到类和方法都连接进App里,所以精简SDK大小很有必要,有助于减少最终App的size。

iOS平台上库文件格式

库文件主要分动态库和静态库两种。

动态库:

文件后缀名有.dylib和.framework。 链接时不复制,程序运行时由系统动态加载到内存,供程序调用,系统只加载一次,多个程序共用,节省内存。

静态库:

文件后缀名有.a和.framework。.framework是一个文件包,包含二进制文件、头文件及相关的资源文件。 链接时完整地拷贝至可执行文件中。

游戏SDK文件格式

现在游戏SDK是framework形式的静态库。framework文件夹中二进制文件是占比最大的部分,其格式为“ Mach-O universal binary ”,是一种Fat binary文件。

  1. Fat binary文件由多个cpu平台上的archive文件合并生成。
  2. archive文件是由多个.o文件及调试用的符号文件合并生成。
  3. .o文件即代码源文件编译后生成。

大致关系是 Fat binary包含多个.a文件,.a文件包含多个.o文件。

相关命令行 工具 使用

查看SDK的架构信息

查看SDK文件的架构信息可以使用 file命令 和 lipo命令

file TestSDK.framework/TestSDK
TestSDK.framework/TestSDK: Mach-O universal binary with 2 architectures: [arm_v7:current ar archive] [arm64]
TestSDK.framework/TestSDK (for architecture armv7): current ar archive
TestSDK.framework/TestSDK (for architecture arm64): current ar archive

可以看到TestSDK.framework包含armv7和arm64两种cpu架构的archive文件。下面是lipo命令的使用:

lipo -info TestSDK.framework/TestSDK
Architectures in the fat file: TestSDK.framework/TestSDK are: armv7 arm64

导出.a文件

在SDK文件中包含多种cpu架构的二进制文件,我们可以使用lipo命令导出。

#导出armv7架构的archive文件
lipo -thin armv7 -output TestSDK_armv7.a  TestSDK.framework/TestSDK
#导出arm64架构的archive文件
lipo -thin arm64 -output TestSDK_arm64.a  TestSDK.framework/TestSDK

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

ar命令是建立或修改archive文件,或是从archive文件中抽取文件的命令。

ar -t -v TestSDK_armv7.a
rw-r--r--     501/20       416800 Jul 17 15:09 2018 __.SYMDEF
rw-r--r--     501/20        81488 Jul 17 15:09 2018 ImageViewController.o
rw-r--r--     501/20        58616 Jul 17 15:09 2018 PasswordViewController.o
......

查看.o文件中代码段、数据段等详细的大小信息

size TestSDK.framework/TestSDK
__TEXT  __DATA  __OBJC  others  dec     hex
15479   3040    0       28683   47202   b862    TestSDK.framework/TestSDK(ImageViewController.o) (for architecture armv7)
12043   2132    0       18722   32897   8081    TestSDK.framework/TestSDK(PasswordViewController.o) (for architecture armv7)
......
16696   5772    0       32674   55142   d766    TestSDK.framework/TestSDK(ImageViewController.o) (for architecture arm64)
13020   4092    0       20627   37739   936b    TestSDK.framework/TestSDK(PasswordViewController.o) (for architecture arm64)
......

使用size -m 可以显示更多的Mach-O segments 和 sections大小信息

size  -m TestSDK.framework/TestSDK
Segment : 47207
        Section (__TEXT, __text): 10072
        Section (__TEXT, __gcc_except_tab): 8
        Section (__DATA, __objc_data): 40
        Section (__DATA, __objc_superrefs): 4
        Section (__TEXT, __objc_methname): 2395
        Section (__DATA, __objc_selrefs): 424
        Section (__TEXT, __cstring): 2274
        Section (__DATA, __cfstring): 320
        Section (__DATA, __objc_classrefs): 80
        Section (__DATA, __objc_ivar): 44
        Section (__TEXT, __ustring): 102
        Section (__DATA, __const): 180
        Section (__TEXT, __objc_classname): 122
        Section (__TEXT, __objc_methtype): 506
        Section (__DATA, __objc_const): 1648
        Section (__DATA, __data): 260
        Section (__DATA, __objc_protolist): 20
        Section (__DATA, __objc_classlist): 4
        Section (__LLVM, __bitcode): 1
        Section (__LLVM, __cmdline): 1
        Section (__DATA, __objc_imageinfo): 8
        Section (__DWARF, __debug_str): 8277
        Section (__DWARF, __debug_loc): 2969
        Section (__DWARF, __debug_abbrev): 807
        Section (__DWARF, __debug_info): 8129
        Section (__DWARF, __debug_ranges): 0
        Section (__DWARF, __debug_macinfo): 1
        Section (__DWARF, __apple_names): 2852
        Section (__DWARF, __apple_objc): 252
        Section (__DWARF, __apple_namespac): 36
        Section (__DWARF, __apple_types): 1351
        Section (__DATA, __nl_symbol_ptr): 8
        Section (__DWARF, __debug_line): 4007
        total 47202
total 47207

可以看出size命令是可以直接显示Fat binary文件中各个cpu架构文件信息的,但ar命令不能操作Fat binary文件,只能是某个cpu架构下的archive文件。

如何比较SDK大小变化

结合上面介绍的几个命令行工具,就可以简单比较出SDK文件的大小增量。 思路大致如下:

  1. 使用“lipo -info”命令获取SDK中包含的cpu架构

    lipo -info SDKFilePath | sed -En -e "s/^(Non-|Architectures in the )fat file: .+( is architecture| are): (.*)$/\\3/p"
  2. 使用“lipo -thin”命令抽取出各个cpu架构的.a文件

    lipo -thin arm64 SDKFilePath -output SDKFilePath_arm64.a
  3. 使用“ar -t -v”命令获取SDK中.o文件的大小

    ar -t -v SDKFilePath_arm64.a | awk '{printf \"%s:%s\\n\", $8, $3}'
  4. 比较SDK新旧版本中同一个.o文件大小

按照上述步骤能比对两个SDK中.o文件的变化。 这里有一个脚本 sdkdiff.py

使用方法

sdkdiff.py TestSDK_v1 TestSDK_v2
+1152         361088       Title.o
+824          180032       Notice.o
.....
-4232         260696       Login.o
-5640         243032       Proxy.o
-5856         335680       User.o
----------------------------------
-45288        80801624     Total

如何精简SDK包大小

通过上面命令可以看出,SDK二进制文件包含了不同cpu架构下.m .c .cpp等源码文件编译后生成的.o文件及对应的符号文件。

精简SDK二进制文件大小也主要是删除符号文件,删除没有用到的.o文件,删除无用代码。

删除符号文件

设置Xcode Target的Building Settings >> Deployment >> Deployment Postprocessing 为YES Building Settings >> Deployment >> Strip Style 有三种选项All Symbols、Non-Global Symbols和Debugging Symbols。其中默认是Debugging Symbols。 去掉debug symbols后,不能使用xcode断点调试SDK类的代码,这个对于已发布的release模式的SDK来说,可以去掉。

删除无用类

使用otool -v -s __DATA objc_classlist 可执行文件名, 逆向 DATA.__objc_classlist段,提取可执行文件里所有的OC类名,

使用otool -v -s __DATA objc_classrefs 可执行文件名, 逆向 DATA.__objc_classrefs段,提取可执行文件里所有的引用到的OC类名,

两者的差集就是代码中没有直接使用到的OC类,注意可以通过反射的方式来使用OC类,还需要搜索代码中有没有通过类的名字来使用该OC类。 这里的方法是针对可执行文件,所以在编译好SDK后,还应为SDK编译一个可执行的Demo,然后使用上面的方法来查找未使用到的类。

注意:OC是动态语言,是可以通过类名来调用类的方法的,所以查找到没有引用到的类后,还需要查看代码中有没有通过 NSClassFromString(@“OCClassName”) 方法使用该类。

下面是查找无用类的脚本

#!/bin/bash
# 输出未在项目中使用的类名
# 使用说明
# unused_class <hex file>

usage() {
  cat <<__EOF
Summary
    output names of unused objc class.
Usage
    $(basename $0) <hex file>
__EOF
}

error_usage(){
  local error=${1:-Undefined error}
  echo "$0:$LINE $error"
  usage
  exit 1
}

if [[ $# -lt 1 ]] ;then error_usage "Error! No project directory was specified."; fi

HEXFILE=$1
CLASSLIST=`otool -s __DATA __objc_classlist  $HEXFILE | grep "^[0-9a-f]" | awk '{print $3$2"\n"$5$4}'|sort`
CLASSREFS=`otool -s __DATA __objc_classrefs  $HEXFILE | grep "^[0-9a-f]" | awk '{print $3$2"\n"$5$4}'|sort`

nm $HEXFILE >./temp_all_symbols.txt
for classname in $CLASSLIST
do
    if [[ $CLASSREFS =~ $classname ]]; then 
       :
    else
        grep $classname ./temp_all_symbols.txt
    fi
done
rm ./temp_all_symbols.txt

删除无用代码

使用otool -v -s __DATA objc_selrefs 可执行文件名,逆向 DATA. objc_selrefs段,提取可执行文件里引用到的方法名, 使用LinkMap文件的 TEXT.__text 提取当前可执行文件里所有objc类方法和实例方法,

两者的差集就是代码中没有直接使用到的方法,注意可以通过反射的方式来类方法和实例方法,还需要搜索代码中有没有通过方法名字来使用该方法。

精简SDK引用的第三方库

我们的SDK引用了微信,QQ等第三方库来提供第三方登录的功能,为了SDK接入方的方便,我们直接将微信,QQ等公用的第三方库打包进我们的SDK了。虽然这样减少了我们SDK接入方的工作,但是也导致我们SDK文件大小暴涨。另外也带来了一些其他的问题,比如接入方需要更新微信,QQ SDK版本时就麻烦了。

我们SDK去掉微信,QQ SDK,让接入方自己引入,这样可以减小我们SDK的大小。但是最后所有的接入方生成的App还是会包含微信,QQ的SDK,然而,并不是所有的接入方都需要接入微信,QQ登录。这样的话,对于这部分的接入方,App就包含并不需要的微信和QQ的SDK代码。 其实我们可以在我们的SDK中使用类反射的的方式来调用微信,QQ等第三方库的功能,这样对于没有接入微信登录功能的接入方,就没必要引入微信等第三方库。

+ (void)enableWeChat:(NSString *)appid class:(Class)cls {
  [NSClassFromString(@"WXApi") registerApp:appid];
}

接入方如果需要引入微信登录功能,需要引入微信SDK,然后再添加这样的代码

[TestSDK enableWeChat:@"1234567890"  class:WXApi.class];

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Don't Make Me Think

Don't Make Me Think

Steve Krug / New Riders Press / 18 August, 2005 / $35.00

Five years and more than 100,000 copies after it was first published, it's hard to imagine anyone working in Web design who hasn't read Steve Krug's "instant classic" on Web usability, but people are ......一起来看看 《Don't Make Me Think》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具