iOS 热更新方案简析

HotFix

Posted by PaysonChen on July 25, 2025

1、背景

​ 原生代码不像前端,可以无感更新,当线上出现致命问题时,如果没有做ABTest或者其他远程配置,往往需要通过发版才能解决,而原生发版需要经过应该市场审核,往往流程长(一般需要12小时,甚至24小时以上)。

2、技术方案

​ JSPatch方案被AppStore拒了,只能转向寻求二进制化的热更新方案

​ OCRunner,通过编译二进制补丁,理论上是可以通过AppStore审核(待后续尝试一下)

3、实践步骤

OCRunner

1
git clone --recursive https://github.com/SilverFruity/OCRunner.git

​ 详细介绍可参考:https://github.com/SilverFruity/OCRunner/blob/master/README-CN.md

​ 先制造一段崩溃代码:

1
2
3
4
5
6
7
8
9
10
+ (void)willCrash
{
    NSLog(@"TestCrash will crash");
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSArray *a = @[];
        a[0];
        NSLog(@"TestCrash do not crash");
    });
}

​ 在调用上述代码后,会崩溃,崩溃日志如下:

1
2
3
4
5
6
7
8
9
10
2025-07-25 09:31:03.097029+0800 PSCPatchModule_Example[13445:1707171] PSCTestCrash will crash
2025-07-25 09:31:04.112481+0800 PSCPatchModule_Example[13445:1707171] PSCTestCrash crashing 1
2025-07-25 09:31:04.112714+0800 PSCPatchModule_Example[13445:1707171] PSCTestCrash crashing 2
2025-07-25 09:31:04.112792+0800 PSCPatchModule_Example[13445:1707171] PSCTestCrash crashing 3
2025-07-25 09:31:04.113585+0800 PSCPatchModule_Example[13445:1707171] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArray0 objectAtIndex:]: index 0 beyond bounds for empty NSArray'
*** First throw call stack:
(0x1cb39ed94 0x1c445c3d0 0x1cb4adc94 0x104b584f8 0x1051aa038 0x1051ad1b4 0x1051c3e30 0x1051ba5f8 0x1051ba2dc 0x1cb42dd18 0x1cb40f650 0x1cb4144dc 0x20667435c 0x1cd7a037c 0x1cd79ffe0 0x104b583d4 0x1ea89cdec)
libc++abi: terminating due to uncaught exception of type NSException
*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArray0 objectAtIndex:]: index 0 beyond bounds for empty NSArray'
terminating due to uncaught exception of type NSException

再修复这个代码

1
2
3
4
5
6
7
8
9
10
11
    NSLog(@"PSCTestCrash will crash");
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSArray *a = @[];
        NSLog(@"PSCTestCrash crashing 1");
        NSLog(@"PSCTestCrash crashing 2");
        NSLog(@"PSCTestCrash crashing 3");
		
		//去掉崩溃代码后
//        a[0];
        NSLog(@"PSCTestCrash do not crash");
    });

执行编译二进制修复文件:

1
./PatchGenerator -files PSCTestCrash.m -refs Scripts.bundle -output binarypatch

​ 日志输出:

1
2
3
4
5
2025-07-25 09:39:17.113022+0800 PSCPatchModule_Example[13424:1699794] PSCTestCrash will crash
2025-07-25 09:39:18.152015+0800 PSCPatchModule_Example[13424:1699794] PSCTestCrash crashing 1
2025-07-25 09:39:18.152216+0800 PSCPatchModule_Example[13424:1699794] PSCTestCrash crashing 2
2025-07-25 09:39:18.152286+0800 PSCPatchModule_Example[13424:1699794] PSCTestCrash crashing 3
2025-07-25 09:39:18.152352+0800 PSCPatchModule_Example[13424:1699794] PSCTestCrash do not crash

4、Demo

github : https://github.com/Payson-Chen/PSCPatchModule