OCRunnerArm64 1.0.19

OCRunnerArm64 1.0.19

Silver 维护。



  • SilverFruity

OCRunner

中文介绍

Wiki

介绍

使用 OCRunner 生成补丁的工作流程

image

所有参与方的责任

  • oc2mangoLib 等同于一个简单的编译器,负责生成抽象语法树。
  • ORPatchFile 负责序列化和解序列化抽象语法树,并确定版本是否可用。
  • PatchGenerator 负责集成 oc2mangoLib 和 ORPatchFile 的功能。(所有上述工具都在 oc2mango 项目中)。
  • OCRunner 负责执行抽象语法树。

与其他热修复库的区别

  • 使用二进制补丁文件。提高安全性、减小补丁大小、优化启动时间,且可在PatchGenerator阶段优化。

  • 自定义 Arm64 ABI (你还可以选择使用 libffi)

  • 完全支持 Objective-C 语法,但不支持预编译和部分语法。

使用OCRunner本地运行补丁

OCRunnerDemo可以作为整个过程的参考。

Cocoapods

pod 'OCRunner'      #Support all architectures, including libffi.a
# or
pod 'OCRunnerArm64' #Only supports arm64 and arm64e, does not include libffi.a

下载 PatchGenerator

解压 PatchGenerato.zip,然后将 PatchGenerator 保存到 /usr/bin/ 或项目目录中。

为PatchGenerator添加运行脚本

  1. 项目设置 -> 构建阶段 -> 点击左上角的 + -> 新建运行脚本阶段

  2. 为[PatchGenerator文件路径] -files [Objective-C源文件或目录] -refs [Objective-C头文件或目录] -output [保存补丁的路径]

  3. 例如:OCRunnerDemo中的运行脚本

    $SRCROOT/OCRunnerDemo/PatchGenerator -files $SRCROOT/OCRunnerDemo/ViewController1 -refs  $SRCROOT/OCRunnerDemo/Scripts.bundle -output $SRCROOT/OCRunnerDemo/binarypatch

开发环境:执行补丁文件

  1. 将生成的补丁文件作为资源文件添加到项目中。

  2. Appdelegate.m

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
#if DEBUG
    NSString *patchFilePath = [[NSBundle mainBundle] pathForResource:@"PatchFileName" ofType:nil];
#else
   // download from server
#endif
    [ORInterpreter excuteBinaryPatchFile:patchFilePath];
    return YES;
}
  1. 每次修改文件时,请记得使用 Command+B,调用 运行脚本 重新生成补丁文件。

在线环境

  1. 将补丁上传到资源服务器。
  2. 在应用中下载并保存补丁文件。
  3. 使用 [ORInterpreter excuteBinaryPatchFile:PatchFilePath] 执行补丁。

使用说明

结构、枚举、类型定义

您可以修改 OCRunnerDemo 中的 ViewController1 来运行以下代码。

// A new type called dispatch_once_t will be added
typedef NSInteger dispatch_once_t;
// link NSLog
void NSLog(NSString *format, ...);

typedef enum: NSUInteger{
    UIControlEventTouchDown                                         = 1 <<  0,
    UIControlEventTouchDownRepeat                                   = 1 <<  1,
    UIControlEventTouchDragInside                                   = 1 <<  2,
    UIControlEventTouchDragOutside                                  = 1 <<  3,
    UIControlEventTouchDragEnter                                    = 1 <<  4
}UIControlEvents;

int main(){
    UIControlEvents events = UIControlEventTouchDown | UIControlEventTouchDownRepeat;
    if (events & UIControlEventTouchDown){
        NSLog(@"UIControlEventTouchDown");
    }
    NSLog(@"enum test: %lu",events);
    return events;
}
main();

提示

建议创建一个新文件来放置以上代码,类似于 OCRunnerDemo 中的 UIKitRefrence 和 GCDRefrence 文件,然后将补丁生成以 -links 的形式添加。

使用系统内置的 C 函数

//you only need to add the C function declaration in Script.
//link NSLog
void NSLog(NSString *format, ...);

//then you can use it in Scrtips.
NSLog(@"test for link function %@", @"xixi");

您可以修改 OCRunnerDemo 中的 ViewController1 的内容来运行代码。

当你在脚本中添加此代码时,OCRunner 将使用 ORSearchedFunction 来搜索函数名的指针。它的核心是 SymbolSearch (来自 fishhook 编辑).

如果函数名的搜索结果为 NULL,OCRunner 将在控制台像这样提醒您

|----------------------------------------------|
|❕you need add ⬇️ code in the application file |
|----------------------------------------------|
[ORSystemFunctionTable reg:@"dispatch_source_set_timer" pointer:&dispatch_source_set_timer];

修复Objective-C 的对象(类)方法并添加属性

若要修复方法,可以仅重写该方法,而不实现其他方法。

@interface ORTestClassProperty:NSObject
@property (nonatomic,copy)NSString *strTypeProperty;
@property (nonatomic,weak)id weakObjectProperty;
@end
@implementation ORTestClassProperty
- (void)otherMethod{
    self.strTypeProperty = @"Mango";
}
- (NSString *)testObjectPropertyTest{
  	[self ORGtestObjectPropertyTest] // Add'ORG' before the method name to call the original method
    [self otherMethod];
    return self.strTypeProperty;
}
@end

使用块解决循环引用

__weak id object = [NSObject new];
// Minimal block
void (^a)(void) = ^{
    int b = 0;
};
a();

使用 GCD

其本质是 使用系统内置的C函数。通过OCRunnerDemo中的 GCDRefrences 文件添加,其中包含所有与GCD相关的函数声明和typedef。

例如

// link dispatch_sync
void dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
void main(){
  dispatch_queue_t queue = dispatch_queue_create("com.plliang19.mango",DISPATCH_QUEUE_SERIAL);
	dispatch_async(queue, ^{
   	completion(@"success");
	});
}
main();

使用内联函数、预编译函数

// Inline function: just add a global function in the patch, such as `CGRectMake` in UIKitRefrences
CGRect CGRectMake(CGFloat x, CGFloat y, CGFloat width, CGFloat height)
{
  CGRect rect;
  rect.origin.x = x; rect.origin.y = y;
  rect.size.width = width; rect.size.height = height;
  return rect;
}
// Pre-compiled function: you need to add the following code in the App
[[MFScopeChain top] setValue:[MFValue valueWithBlock:^void(dispatch_once_t *onceTokenPtr,
                                                                  dispatch_block_t _Nullable handler){
        dispatch_once(onceTokenPtr,handler);
    }] withIndentifier:@"dispatch_once"];

如何确定源文件是否包含在补丁中

image

性能测试

加载时间

2

执行速度和内存使用

设备:iPhone SE2,iOS 14.2,Xcode 12.1。

以经典的斐波那契数列函数为例,查找第25项的测试结果

JSPatch

  • 执行时间,平均时间为0.169秒

  • 内存使用稳定在约12MB

OCRunner

  • 执行时间,平均时间为1.05秒

  • 内存使用,峰值约为60MB

Mango

  • 执行时间,平均时间为2.38秒

  • 内存使用持续上升,最高达到约350MB。

  • 在测试递归函数时,OCRunner的性能是JSPatch的1/5,是Mango的2.5倍。
  • OCRunner的补丁加载速度约为Mango的20倍以上,并且随着补丁大小的增加而增加,而JSPatch的值未知。
  • 关于递归方法调用时的内存使用问题,目前存在过度使用的问题。在查找斐波那契数列的第30项时,Mango将崩溃,而OCRunner的峰值内存使用约为600MB。

当前问题

  1. 指针和乘号识别冲突,衍生问题:类型转换等。
  2. 不支持静态、内联函数声明。
  3. 不支持C数组声明:type a[]、type a[2]、value = { 0 , 0 , 0 , 0 }。
  4. 不支持'-'符号操作符...
  5. 不支持固定C函数。

支持的语法

  1. 类声明和实现,支持类别。
  2. 协议
  3. struct、enum、typedef
  4. 使用函数声明链接系统函数指针
  5. 全局函数
  6. 多参数调用(方法和函数)
  7. *&(指针运算)
  8. 变量静态关键字
  9. NSArray: @[value1, value2],NSDictionary: @{ key: value }, NSNumer: @(value)
  10. NSArray、NSDictionary值和赋值语法:id value = a[var]; a[var] = value;
  11. 操作符,除'-'符号外,均已实现

等等。

感谢