OCRunner 1.3.3

OCRunner 1.3.3

Silver 维护。



OCRunner 1.3.3

  • SilverFruity

OCRunner QQ群: 860147790

中文介绍

相关文章

Wiki

简介

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

image

各方的职责

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

与其他热补丁库的区别

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

  • 自定义Arm64 ABI(也可选择使用libffi)

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

使用OCRunnerDemo在本地运行补丁

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

无法通过下载ZIP文件成功运行。您必须使用以下shell命令来运行OCRunnerDemo。

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

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/local/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. 在 App 中下载并保存补丁文件。
  3. 使用 [ORInterpreter excuteBinaryPatchFile:PatchFilePath] 执行补丁。

使用说明

结构,枚举,类型定义

您可以通过修改 ViewController1 中的代码在 OCRunnerDemo 中运行以下代码。

// 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

使用 Block 并解决循环引用

__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. 运算符,除了'->'外都已实现

等等。

感谢