写点什么

Instagram 的 iOS 原生代码热加载技术

2018 年 5 月 10 日

在 Instagram 和 Facebook,React Native 让我们能够以更快的速度推进迭代产品。在模拟器上使用⌘R 快捷键就可以查看更改效果。然而使用原生代码进行开发,每次都需要重新编译、链接、安装、启动等一系列步骤才能查看更改效果。

即便是一个小项目,这样的开发流程也会让开发者感到很繁琐。在我们的项目规模上,结果就是工程师要花费大量的时间来等待电脑完成构建任务,等待的过程中又经常会切换到其他应用,从而浪费了更多的时间。

去年年底我们着手将 React Native 的开发体验带入 Objective-C 原生代码的开发中,目的是让工程师能及时看到代码更改效果。为了实现这一点,我们通过在运行时加载新代码来避免重启应用。

如何重新加载原生代码

为了重新加载原生代码,第一步需要将修改过的代码转换成正在运行的应用可以加载的动态库。这一步操作非常快,该操作以文件为单位进行:只编译有改动的文件,并链接为一个动态库。

iOS 真机设备不允许应用加载其自身 bundle 以外的动态库。同时也不允许应用向其自身 bundle 写文件。因此可加载的动态库必须在应用安装时就已经存在其 bundle 里,因此无法在真机上加载新的动态库。

幸运的是,iOS 模拟器上并没有这些限制。每个模拟器的文件系统其实就是 Mac 文件系统里的一个文件夹。另外,iOS 模拟器中的 dlopen 函数不会按文件位置进行过滤,这使得无论如何都可以直接将动态库写入应用程序的 bundle 中。

我们在程序代码中新增了一个启动方法,该方法会在 debug 模式下生成一个后台任务来监听指定文件夹的内容变化。动态库创建好后会移动到指定文件夹。这会触发应用中设置好的回调,执行以下几个步骤:

  • 加载新的动态库。
  • 确定添加了哪些 Objective-C 类。
  • 查找这些类的原始版本。Objective-C 类本身也是对象,很容易通过名字找到。
  • 使用 Objective-C 运行时 APIs 来替换这些原始类的方法实现为新的实现。这会替换类所有的方法实现,因为我们无法确定哪些方法实现有改动。
  • 将执行结果写回相同目录以便工具读取。

到这里我们的新代码已经在应用中跑起来了,下一次调用这些方法就会执行我们的新代码。当然,这只有在 iOS 模拟器上做开发时可用,真机和生产环境是不可行的。

构建开发体验

重新安装、重启应用来查看更改效果很慢,但简单可靠。在 Xcode 中按下按钮或快捷键,最终,代码的结果会显示在屏幕上。我们在我们的插件工具中做了类似的事情:在 Xcode 的工具栏放置了一个按钮和快捷键来触发加载新的代码。

与重启应用相比我们还有另一个优势:保持应用的当前状态。重启会重置应用回到最初的入口 - 对我们的应用来说是 feeds 首页。负责其他模块的工程师必须重新点击进入到自己的模块才能查看他们的修改是否正确。

然而,新的代码加载后,什么也不会发生:应用之前的状态不会受到影响。幸运的是,在 Facebook 上使用的声明式 iOS UI 框架 ComponentKit 具有执行即时重新布局的有用方法: + [CKComponentDebugController reflowComponents] 。使用 ComponentKit 构建的视图可以在新代码加载后调用该方法,来让布局代码重新执行并,并在屏幕上立即更新。结果就是,使用 ComponentKit 进行布局,工程师可以及时查看布局的修改。以前,工程师需要批量更新并考虑何时需要重启。 现在他们可以交互式地迭代工作。

总结

我们的工具在小组内已变得非常流行,特别是使用 ComponentKit 的工程师。在大型应用中,这使得工程师查看修改结果的速度提升了 20 倍。高性能、与现有开发工具和框架的紧密结合已经让原生代码的加载成了在 Instagram 和 Facebook 的 iOS 工程师平时工作流程中的重要一环。

Nate Stedman 一名来自 Instagram 纽约的 iOS 工程师。

感谢覃云对本文的审校。

2018 年 5 月 10 日 18:241524

评论

发布
暂无评论
发现更多内容

还在为算法烦恼?那你应该还没看过这份Git上70k标星的笔记

Java架构师迁哥

在深夜加油站遇见哈利波特

脑极体

【Knative系列】一文读懂 Knative Serving扩缩容的原理

Chumper

Serverless knative autoscaler kantive

Week 5命题作业

balsamspear

极客大学架构师训练营

交易所开发技术,数字货币交易系统源码搭建

135深圳3055源中瑞8032

Android 一行代码接入扫码功能 (CameraX + zxing)

Java android kotlin zxing camera

手动造轮子——为Ocelot集成Nacos注册中心

yi念之间

nacos ASP.NET Core Ocelot

区块链+能源 大放异彩

CECBC区块链专委会

区块链 能源

银行数字化转型:需建立起以体验为核心、数据为基础、技术为驱动的架构体系

CECBC区块链专委会

银行 数字经济

「混合云」会是云计算的下一个战场吗?

ToB行业头条

阿里云

甲方日常 45

句子

工作 随笔杂谈 日常

Dubbo-go Server端开启服务过程

apache/dubbo-go

dubbo dubbo-go dubbogo

架构师第一期作业(第 7 周)

Cheer

课程作业

区块链数字货币钱包开发模式,深圳数字钱包开发

135深圳3055源中瑞8032

广东云算力矿机系统开发,挖矿平台搭建

135深圳3055源中瑞8032

USDT支付系统开发方案,区块链币支付系统搭建

135深圳3055源中瑞8032

DDIA 读书笔记(5)数据分区方案

莫黎

读书笔记

英特尔首批独显笔记本亮相,非凡S3x纵享轻薄新体验

intel001

Week 7 命题作业

阿泰

书写高质量SQL的30条建议

诸葛小猿

MySQL SQL优化

央行数字货币为人民币国际化之路提供推动力

CECBC区块链专委会

数字货币

直播预告 | 应用加固防破解,4.1折就够了

蚂蚁集团移动开发平台 mPaaS

安全攻防 App风险 mPaaS

high-performance-tidb-challenge 记录

王传义

天源迪科受邀出席“第四届央企电商化采购发展高峰论坛"

DT极客

Week 5学习总结

balsamspear

极客大学架构师训练营

手动造轮子——基于.NetCore的RPC框架DotNetCoreRpc

yi念之间

RPC ASP.NET Core

阿里云视频云技术专家 LVS 演讲全文:《“云端一体”的智能媒体生产制作演进之路》

阿里云视频云

媒体 音视频

架构师训练营第一期 - week7

习习

darknet A版安装

Dreamer

【性能优化】纳尼?内存又溢出了?!是时候总结一波了!!

冰河

性能优化 内存泄露 高并发 高性能 内存溢出

响应式编程简介之:Reactor

程序那些事

响应式编程 reactor Reactive 程序那些事 响应式系统

InfoQ 极客传媒开发者生态共创计划线上发布会

InfoQ 极客传媒开发者生态共创计划线上发布会

Instagram的iOS原生代码热加载技术-InfoQ