NoCompile

iOS / macOS 热重载 · C · C++ · Objective-C · Swift

改完代码,按下 ⌘S,
它已经在 App 里生效

NoCompile 只重新编译你刚改动的那一个文件,链接成 dylib 注入正在运行的 App,把新实现换上去。不用停 App,也不用等整个工程重新编译。

最新版本 v0.2.13 · macOS 12+ · Apple 公证 · Sparkle 自动更新 · 客户端 SDK MIT 开源

演示动画。示例工程实测:单个 Swift / Objective-C / C 文件保存后 0.3~0.8 秒生效,整个过程通常在 1~3 秒内完成。

01 · 原理

一次保存,五步注入

Mac 端监听文件变化,只重编改动的文件;App 进程内的客户端收到 dylib 后完成替换。模拟器与 macOS 走本机 TCP,真机走 USB(usbmuxd)。

  1. ⌘SFSEvents 捕获保存
  2. swiftc -###从构建日志还原单文件编译命令
  3. .dylib只编译这一个文件并链接
  4. codesign用 App 的身份签名后推送
  5. dlopen进程内替换为新实现
class_replaceMethodObjective-C 方法替换
vtableSwift 类的虚函数表覆写
GOT rebindC / C++ 自由函数与符号重绑

02 · 特性

为日常开发打磨的热重载

四种语言,一套机制

C、C++、Objective-C、Swift 统一支持:ObjC 方法、Swift 类方法、C/C++ 自由函数都能替换,后者通过 GOT 重绑覆盖。

CC++Objective-CSwiftSwiftUI

编译命令跟随 Xcode

解析构建日志拿到 driver 命令,再交给官方 swiftc -### 展开单文件命令。参数由编译器自己生成,Xcode 升级自动跟随。

保存即注入

FSEvents 自动触发,无需按快捷键。漏检时点「检查改动」或按全局快捷键 ⌃⌥⌘R 补上。

真机走 USB

以 usbmuxd 为主通道连接真机,不依赖局域网广播;从构建日志自动提取签名身份。

资源与新文件

storyboard、xib、图片保存即生效(含真机);运行中新建的源文件也能注入。

零侵入刷新

业务类不必实现 injected()。已打开的页面退出重进即可看到,也可开启自动重建;SwiftUI 配合 @ObserveInjection 监听注入通知自动重绘。

类型判定不失效

登记新旧类的等价关系,注入后 isKindOfClass: 与 as? 照常工作。

中文与空格路径

全程用参数数组调用子进程,不拼接 shell 字符串,工程放在任何目录都能用。

布局变化时主动保护

增删存储属性、调换 Swift 方法顺序、SwiftUI 视图结构变化会改变内存布局。NoCompile 检测到后不替换该类,并在日志与 App 浮层提示重新运行;同文件里其他类和函数照常生效。SwiftUI 在 body 末尾加 .enableInjection() 即可热重载结构改动。

03 · 控制台

每一次注入都看得见

左侧是已连接的 App 与最近的注入记录(成功 / 失败数、平均耗时),右侧是按类别着色、可筛选搜索的日志。编译失败时右键即可在 Xcode 中定位到出错行;遇到问题可一键导出诊断包。

NoCompile 控制台与 iOS 模拟器中的示例 App:分别修改 Swift、Objective-C、C 文件并保存,0.3~0.8 秒即在运行中的 App 里生效

04 · 接入

两步接入,只在 Debug 生效

客户端 SDK 就在本仓库,零第三方依赖,支持 iOS / tvOS 15+ 与 macOS 12+。

1

Debug 配置添加链接参数

Build Settings → Other Linker Flags,只给 Debug 填写。它让 App 内部的函数调用走 GOT 间接寻址,运行时才能重定向到新实现。CocoaPods 方式会自动加上。

-Xlinker -interposable
2

接入客户端,任选一种

以下方式都支持真机,镜像加载或 load() 时自动启动,无需写启动代码。

Xcode:File → Add Package Dependencies,填入下面的地址,把 NoCompile 库加到 App target。

https://github.com/wangwanjie/NoCompile

或写进 Package.swift:

.package(url: "https://github.com/wangwanjie/NoCompile", from: "0.2.13"),
// target 的 dependencies
.product(name: "NoCompile", package: "NoCompile")

Debug 的 Other Linker Flags 再加 -ObjC,确保客户端的类被完整链接。模块名为 NoCompileClient。

3

选择工程,运行 App

在 Xcode 完整构建过一次后,点菜单栏 ⚡ → 选择工程,再 ⌘R 运行 App。之后编辑任意源文件并保存即可。

完整接入指南:Mac Catalyst、DYLD_INSERT_LIBRARIES、真机排查 →

下载 NoCompile

按芯片选择安装包,拖入「应用程序」即可。安装包已经过 Apple 公证,之后通过 Sparkle 自动更新,只接收同架构的更新。

不确定芯片类型:苹果菜单 → 关于本机,「芯片」显示 Apple M 系列选 arm64,显示 Intel 选 x86_64。每个版本附带 SHA256SUMS.txt 供核对。

所有版本与更新说明