《不给“爸爸”添麻烦

来源:互联网 发布:生物软件下载 编辑:程序博客网 时间:2024/04/26 10:49

本文来自于腾讯Bugly公众号(weixinBugly), 作者:hillsonsong,未经作者同意,请勿转载,原文地址:https://mp.weixin.qq.com/s/IIlNVk2ZCv2rf5dUH6NT-A

一、背景

苹果官方文档 对提交商店 APP 的二进制文件中 __TEXT 段大小有限制,超过大小限制的应用在提交评审的时候会被拒绝。目前 Ngame 在合入海外潘多拉 SDK 的过程中,发现二进制 __TEXT 段大小超过限制,因此需要对应用进行瘦身。

这里写图片描述

  • Link Map 文件是 Xcode 产生可执行文件的同时生成的链接信息,用来描述可执行文件的构造成分,包括代码段(__TEXT)和数据段(__DATA)的分布情况。

  • Link Map 里展示了整个可执行文件的全貌,列出了编译后的每一个.o目标文件的信息(包括静态链接库.a里的),以及每一个目标文件的代码段,数据段存储详情。

  • 设置 Project->Build Settings->Write Link Map File 为YES,并设置 Path to Link Map File,build 完后就可以在设置的路径看到 Link Map 文件了。

这里写图片描述

通过 Link Map File 精确统计出各个组件 __TEXT 段大小,因此有必要对 Link Map File 稍做研究。每个 Link Map 文件由3个部分组成:

1、Object files:目标文件列表

[  4] /Users/hilson/Desktop/IMSDKDemo/IMSDKDemo/IMSDKCoreKit.framework/IMSDKCoreKit(IMSDKUtils.o)[  5] /Users/hilson/Desktop/IMSDKDemo/IMSDKDemo/IMSDKCoreKit.framework/IMSDKCoreKit(IMSDKAFSecurityPolicy.o)[  6] /Users/hilson/Desktop/IMSDKDemo/IMSDKDemo/IMSDKCoreKit.framework/IMSDKCoreKit(IMSDKPayExtendManager.o)[  7] /Users/hilson/Desktop/IMSDKDemo/IMSDKDemo/IMSDKCoreKit.framework/IMSDKCoreKit(IMSDKAccountForOC.o)[  8] /Users/hilson/Desktop/IMSDKDemo/IMSDKDemo/IMSDKCoreKit.framework/IMSDKCoreKit(IMSDKLocationManager.o)

前面中括号表示该文件的编号,IMSDKCoreKit 静态库中所有目标文件都会列出来(包括私有文件)。

2、Sections:段表,描述各个段在最后编译成的可执行文件中的偏移位置和大小,包括了代码段(__TEXT,保存程序代码段编译后的机器码)和数据段(__DATA,保存变量值)

# Sections:# Address    Size        Segment    Section0x1000046B4    0x0007B238    __TEXT    __text0x10007F8EC    0x00000BDC    __TEXT    __stubs0x1000804C8    0x00000BDC    __TEXT    __stub_helper0x1000810A4    0x0000C2CA    __TEXT    __objc_methname0x10008D36E    0x00000ACE    __TEXT    __objc_classname0x10008DE3C    0x0000AFAF    __TEXT    __objc_methtype0x100098DEC    0x0000EE48    __TEXT    __gcc_except_tab0x1000A7C34    0x00009E1C    __TEXT    __cfstring0x1000B1A50    0x00000180    __TEXT    __const0x1000B1BD0    0x00000242    __TEXT    __ustring0x1000B1E14    0x000021B0    __TEXT    __unwind_info0x1000B3FC8    0x00000034    __TEXT    __eh_frame0x1000B4000    0x00000258    __DATA    __got0x1000B4258    0x000007E8    __DATA    __la_symbol_ptr0x1000B4A40    0x00003520    __DATA    __const0x1000B7F60    0x000080C0    __DATA    __cfstring0x1000C0020    0x000002A0    __DATA    __objc_classlist0x1000C02C0    0x00000008    __DATA    __objc_nlclslist0x1000C02C8    0x00000060    __DATA    __objc_catlist0x1000C0328    0x00000010    __DATA    __objc_nlcatlist0x1000C0338    0x00000108    __DATA    __objc_protolist0x1000C0440    0x00000008    __DATA    __objc_imageinfo0x1000C0448    0x00015CA0    __DATA    __objc_const0x1000D60E8    0x000031F0    __DATA    __objc_selrefs0x1000D92D8    0x00000050    __DATA    __objc_protorefs0x1000D9328    0x00000498    __DATA    __objc_classrefs0x1000D97C0    0x000001F8    __DATA    __objc_superrefs0x1000D99B8    0x000006AC    __DATA    __objc_ivar0x1000DA068    0x00001A40    __DATA    __objc_data0x1000DBAA8    0x00000CB0    __DATA    __data0x1000DC758    0x00000278    __DATA    __bss0x1000DC9D0    0x00000038    __DATA    __common

首列是数据在文件的偏移位置,第二列是这一段占用大小,第三列是段类型,代码段和数据段,第四列是段名称。

__text表示编译后的程序执行语句,__data表示已初始化的全局变量和局部静态变量,__bss表示未初始化的全局变量和局部静态变量,__cstring表示代码里的字符串常量。

3、Symbols:详细描述按每个文件列出每个对应字段的位置和占用空间

# Symbols:# Address    Size        File  Name0x1000046B4    0x00000050    [  1] -[ViewController viewDidLoad]0x100004704    0x00000050    [  1] -[ViewController didReceiveMemoryWarning]0x100004754    0x000000A4    [  2] _main0x1000047F8    0x0000013C    [  3] -[AppDelegate application:didFinishLaunchingWithOptions:]0x100004934    0x0000004C    [  3] -[AppDelegate applicationWillResignActive:]0x100004980    0x0000004C    [  3] -[AppDelegate applicationDidEnterBackground:]0x1000049CC    0x0000004C    [  3] -[AppDelegate applicationWillEnterForeground:]0x100004A18    0x0000004C    [  3] -[AppDelegate applicationDidBecomeActive:]0x100004A64    0x0000004C    [  3] -[AppDelegate applicationWillTerminate:]0x100004AB0    0x0000002C    [  3] -[AppDelegate window]0x100004ADC    0x0000004C    [  3] -[AppDelegate setWindow:]0x100004B28    0x00000044    [  3] -[AppDelegate .cxx_destruct]0x100004B6C    0x00000244    [  4] +[IMSDKUtils getAppPlistInfo]

同样首列是数据在文件的偏移地址,第二列是占用大小,第三列是所属文件序号,对应上述Object files列表,最后是名字。

三、代码段大小统计

1.如上文提到的,每个文件都有一个固定的标号,如 IMSDKUtils 这个目标文件的标号是4 [4]/Users/hilson/Desktop/IMSDKDemo/IMSDKDemo/IMSDKCoreKit.framework/IMSDKCoreKit(IMSDKUtils.o)

2.遍历 Link Map 文件第三部分 Symbols 里的每一行,将文件编号(第三列)为[4] 的数据都取出,将每一行的 size(第二列)相加,就是这个目标文件的大小;

3.静态库中全部目标文件的大小相加,就是这个第三方库的占用空间大小;
目前业界有数款统计小工具,如 nodejs 脚本 ,Link Map解析工具-MAC。

四、优化思路

虽然 IMSDK 代码段已经足够小,对项目影响较小,本着负责到底实事求是的态度,还是有必要对静态库大小瘦身做进一步研究。

1、 代码级别优化

  • 查找无用selector,以往C++在链接时,没有被用到的类和方法是不会编进可执行文件里。但 Objctive-C 不同,由于它的动态性,它可以通过类名和方法名获取这个类和方法进行调用,所以编译器会把项目里所有OC源文件编进可执行文件里,哪怕该类和方法没有被使用到。

  • 查找无用oc类,与查找无用 selector 情况类似

  • 扫码重复代码,可以利用第三方工具simian扫描

整体而言,代码层面优化见效甚微。与此同时,删除重复代码会导致代码重构,极有可能影响代码的稳定性,而且由于 Objctive-C 的 runtime 机制,我们无法从 Link Map 文件中确认某个类和selector 究竟有没在某个特殊情况下通过反射机制调用到。基于稳定性考虑,IMSDK 暂时不采用 代码级别优化

2、编译器优化级别

  • Build Settings->Optimization Level有几个编译优化选项,release版应该选择 Fastest, Smalllest,这个选项会开启那些不增加代码大小的全部优化,并让可执行文件尽可能小。

    这里写图片描述

  • 实测,开启 Fastest, Smalllest 选项后,* __TEXT* 段大小能裁剪 25%。比如,IMSDKWebView __TEXT 段大小从 249.93K 缩减到179.93K,IMSDKHelp __TEXT 段大小从 458.77K 缩减到 336.66K;

  • iMSDK 组件发布版本默认已经选择 Fastest, Smalllest 模式,因此此方案对 iMSDK 组件暂无提升,但可以提供思路给其他 SDK 做瘦身。

3、静态库改用动态库

  • 从 iOS 8 开始,由于 Extendsion 的出现,苹果开始允许自建动态库并在 iOS APP 中引用,这样宿主 APP 和插件之间共享动态库;

  • 从目前来看,iOS 仍然不允许进程间共享动态库,即 iOS 上的动态库只能是私有的,因为我们仍然不能将动态库文件放置在除了自身沙盒以外的其它任何地方;

  • 另外,苹果沙盒会验证动态库的签名,所以如果是动态从服务器更新的动态库,是签名不了的,因此应用插件化、软件版本实时模块升级等功能在 iOS 上无法实现;

  • 由于动态库在应用编译打包的时候,仅把链接信息编译到应用二进制可执行文件中,将 framework 的加载推迟到运行时,因此,应用在提交评审时的代码段大小计算,是不会将动态库的代码段计算计算在内,从而能够节省出一大截代码段大小空间。

这里写图片描述

五、改动汇总

1.IMSDK 统一修改打包脚本,将 framework 的 MACH_O_TYPE 从 staticlib 改成 mh_dylib ,并且 将 GCC_SYMBOLS_PRIVATE_EXTERN 赋值为 NO (在 Build settings 将 “Symbols Hidden By Default“ 为 NO );

2.IMSDKCoreKit(动态库) 使用了 libmtasdk.a(静态库),并且 libmtasdk.a 自建系统类的类别 category,通过 nm 命令可以看到动态库并不会把静态库中所有的 Objective-C 类和类都加载到最后的可执行文件中,会导致运行 crash。因此,需要在 IMSDKCoreKit target 的 Other linker flags 新增 -ObjC 选项。

3.项目工程 iOS 最低系统版本支持从 iOS 7 提升到 iOS 8(根据腾讯移动分析-数据中心数据的统计,iOS 7用户群里已经基本忽略不计)

4.项目工程 将 iMSDK 各个组件从 Garenal->Linked Frameworks and Libraries 添加到 Garenal->Embedded Binaries(When should we use “embedded binaries” rather than “Linked Frameworks” in Xcode?(https://stackoverflow.com/questions/32675272/when-should-we-use-embedded-binaries-rather-than-linked-frameworks-in-xcode))

六、 效果

根据 IMSDK Demo 测试结果,静态库改用动态库后,结论如下

1、IMSDKCoreKit 动态库能和静态库的插件混用,业务可以根据情况自由选择动态库更新;

2、安装包大小会对应增加,因为动态库 SDK 没有编译到应用可执行二进制文件里,而是类似资源的形式以一个单独 framework 文件存在安装包中,导致安装包大小压缩有限。

这里写图片描述

3、IPA 可执行二进制文件体积大大减少,动态库的代码段信息只有符号链接信息,大小基本可以忽略不计。

这里写图片描述

七、常见问题

1.打包的时候出现 Failed to verify bitcode in xxxxx

这里写图片描述

解决方法:在 build setting 中关闭 Enable Bitcode 配置项

2.Found an unexpected Math-O Header

这里写图片描述

解决方法:将 Embedded Binaries 中的静态库移到 Linked Frameworks and Libraries 中

八、参考链接

https://developer.apple.com/library/content/documentation/DeveloperTools/Conceptual/DynamicLibraries/000-Introduction/Introduction.html#//apple_ref/doc/uid/TP40001908-SW1

https://stackoverflow.com/questions/30173529/what-are-embedded-binaries-in-xcode

https://stackoverflow.com/questions/32675272/when-should-we-use-embedded-binaries-rather-than-linked-frameworks-in-xcode

https://mta.qq.com/mta/data/device/os

https://github.com/huanxsd/LinkMap

http://www.cocoachina.com/ios/20151211/14562.html

阅读全文
'); })();
0 0
原创粉丝点击
热门IT博客
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 白图片纯白 纯白底图片 纯白花嫁 纯白魔女吧 纯白轮舞曲 纯白英短 纯白的图片 纯白之恋 aj纯白 纯白的图 纯白蠢白 纯白背景图片 纯白图片无字 一张纯白照片 圣学院骑士纯白在线播放 圣学院骑士纯白在线播放全集 纯白美肌连续中出 纯白魔法少女 撕裂纯白之后金银花露 纯白图加一颗小爱心 小乔纯白花嫁 纯白背景图片高清 纯白图片全白 纯白图片高清 黑黑 纯真年代 纯真时代 纯真 纯真的年代 非纯真年代 纯真书舍 唯美纯真 逆流纯真 纯真逆流时代 纯真年代姜汉娜 我们纯真的青春 我们的纯真年代 我们纯真年代 逆流纯真时代 纯真年代涂磊节目保安完整 韩剧纯真时代在线看