作者简介
东海 ,携程移动开发专家,专注于移动端框架、移动端性能。
元帅,携程资深软件工程师,致力于平台基建开发。
一、背景
现在各大公司的APP都采用的是组件化架构,组件化架构带来了高内聚、低耦合、平台化等诸多有点,使工程结构更加清晰,工程管理更加轻松。iOS工程大多采用CocoaPod进行组件化管理,一些大型的项目需要打包平台来执行组件打bundle包和APP打测试包的任务,在开发方面会采用二进制与源码切换的方式来提高编译速度。
组件化虽然对APP项目的工程管理带来了巨大的好处,但是对开发人员来讲,存在着一些繁琐的问题:
- 在开发中,如果需要调试未解开源码的组件,就需要重新执行命令解开相应组件的源码才能进行调试。
- 每切换一次组件的源码,都需要在终端输入一串加了各种参数的命令来执行pod install,手动输入慢,而且容易出错。
- 组件化使得组件颗粒度变得越来越细,每个人所管理的组件数量就会多,每次组件更新都需要在打包平台上进行打包,等组件bundle包打完,再打测试包进行验证。
这些虽然能让工作正常进行,但是繁琐重复的操作却影响了开发人员的开发效率。
二、现状
携程火车票APP一直以来采用的也是组件化管理,在去年改用CocoaPod进行组件化管理,随着业务的迭代和基础建设不断的完善,pod组件也越来越精细化,目前pod组件数量已超60 。
pod组件数量越庞大,开发人员维护的成本也会越高,不仅要管理维护每个pod组件的更新,还要处理pod组件打bundle包问题、测试包打包时间长的问题。上述繁琐、重复、耗时的操作困扰着我们的iOS开发人员。如果能尽可能的对这些开发体验问题进行优化,那么必然会带来开发人员效率的提升。
三、优化方案
为了让开发人员调试代码更加方便,打包测试体验更好,开发过程更加专注,我们做了许多方面的操作优化和技术实践,主要有:
3.1 通过技术手段,实现二进制调试
在开发过程,难免会遇到自己想调试的组件没有解开源码,程序在运行中崩溃但是崩溃在了未解开源码的组件上,自己看到的只是一堆不明所以的汇编代码(图1),无法像源码调试那样看到足够丰富的调试信息。
图1
3.1.1 二进制文件分析
如何才能不解开源码也能调试二进制、崩溃在了二进制组件上也能定位到具体哪一行成了我们新的问题。我们找了各种资料,找到了美团有款zsource的CocoaPod插件可以进行二进制调试,虽未开源,但大致逻辑文章里已经罗列的很清晰,大致原理:
以libXXXX.a二进制文件为例,用 MachOView 来查看二进制文件,以获取到更友好的二进制信息。我们可以看到 “debug_str” Section 这些信息都存在了二进制的中。debug_str在编译的时候内部会记录源码地址:
图2
使用命令在终端输入:
dwarfdump ./libXXXX.a | grep 'XXXX'
注意到了 AT_name 这个字段名,去DWARF 1.1.0 Reference文档中查阅,我们可以得知:
- 一个DW_AT_name属性,其值是一个以空字符结尾的字符串,其中包含从其派生编译单元的主源文件的完整或相对路径名。
- 一个DW_AT_comp_dir属性,其值是一个以空值结尾的字符串,其中包含编译命令的当前工作目录,该编译命令以某种形式将Forelax视为主机系统,从而生成此编译单元。
XXXX.swift源文件存在这个地址下: /Users/marshal/Desktop/XXXX/XXXX/XXXX.swift
这个地址就是编译时源码所在地址,Debug调试的时候,编译器会先从这里拿对应映射地址去加载源码文件。如果存在对应地址存在源码文件时,就能进入源码调试。
3.1.2 脚本开发
了解基础原理后,那接下来的事情就是解决各种问题障碍:
1)要获取到静态库的源码。
2)获取静态库中存储的编译静态库时源码文件所在的路径。
3)在本地创建上面