苹果官方文档 对提交商店 APP 的二进制文件中__TEXT段大小有限制,超过大小限制的应用在提交评审的时候会被拒绝。目前Ngame在合入海外潘多拉 SDK 的过程中,发现二进制 __TEXT 段大小超过限制,因此需要对应用进行瘦身。
通过 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 Section
0x1000046B4 0x0007B238 __TEXT __text
0x10007F8EC 0x00000BDC __TEXT __stubs
0x1000804C8 0x00000BDC __TEXT __stub_helper
0x1000810A4 0x0000C2CA __TEXT __objc_methname
0x10008D36E 0x00000ACE __TEXT __objc_classname
0x10008DE3C 0x0000AFAF __TEXT __objc_methtype
0x100098DEC 0x0000EE48 __TEXT __gcc_except_tab
0x1000A7C34 0x00009E1C __TEXT __cfstring
0x1000B1A50 0x00000180 __TEXT __const
0x1000B1BD0 0x00000242 __TEXT __ustring
0x1000B1E14 0x000021B0 __TEXT __unwind_info
0x1000B3FC8 0x00000034 __TEXT __eh_frame
0x1000B4000 0x00000258 __DATA __got
0x1000B4258 0x000007E8 __DATA __la_symbol_ptr
0x1000B4A40 0x00003520 __DATA __const
0x1000B7F60 0x000080C0 __DATA __cfstring
0x1000C0020 0x000002A0 __DATA __objc_classlist
0x1000C02C0 0x00000008 __DATA __objc_nlclslist
0x1000C02C8 0x00000060 __DATA __objc_catlist
0x1000C0328 0x00000010 __DATA __objc_nlcatlist
0x1000C0338 0x00000108 __DATA __objc_protolist
0x1000C0440 0x00000008 __DATA __objc_imageinfo
0x1000C0448 0x00015CA0 __DATA __objc_const
0x1000D60E8 0x000031F0 __DATA __objc_selrefs
0x1000D92D8 0x00000050 __DATA __objc_protorefs
0x1000D9328 0x00000498 __DATA __objc_classrefs
0x1000D97C0 0x000001F8 __DATA __objc_superrefs
0x1000D99B8 0x000006AC __DATA __objc_ivar
0x1000DA068 0x00001A40 __DATA __objc_data
0x1000DBAA8 0x00000CB0 __DATA __data
0x1000DC758 0x00000278 __DATA __bss
0x1000DC9D0 0x00000038 __DATA __common
首列是数据在文件的偏移位置,第二列是这一段占用大小,第三列是段类型,代码段和数据段,第四列是段名称。
__text表示编译后的程序执行语句,__data表示已初始化的全局变量和局部静态变量,__bss表示未初始化的全局变量和局部静态变量,__cstring表示代码里的字符串常量。
3、Symbols:详细描述按每个文件列出每个对应字段的位置和占用空间
# Symbols:
# Address Size File Name
0x1000046B4 0x00000050 [ 1] -[ViewController viewDidLoad]
0x100004704 0x00000050 [ 1] -[ViewController didReceiveMemoryWarning]
0x100004754 0x000000A4 [ 2] _main
0x1000047F8 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(https://gist.github.com/bang590/8f3e9704f1c2661836cd) 脚本 ,Link Map解析工具-MAC。
虽然 IMSDK 代码段已经足够小,对项目影响较小,本着负责到底实事求是的态度,还是有必要对静态库大小瘦身做进一步研究。
整体而言,代码层面优化见效甚微。与此同时,删除重复代码会导致代码重构, 极有可能影响代码的稳定性,而且由于 Objctive-C 的 runtime 机制,我们无法从 Link Map 文件中确认某个类和selector 究竟有没在某个特殊情况下通过反射机制调用到。基于稳定性考虑,IMSDK 暂时不采用 代码级别优化。
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(根据腾讯移动分析-数据中心数据(https://mta.qq.com/mta/data/device/os)的统计,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
如果您觉得我们的内容还不错,就请转发到朋友圈,和小伙伴一起分享吧~