惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

M
MIT News - Artificial intelligence
AI
AI
月光博客
月光博客
爱范儿
爱范儿
博客园 - 司徒正美
Last Week in AI
Last Week in AI
博客园 - 三生石上(FineUI控件)
S
Security @ Cisco Blogs
腾讯CDC
W
WeLiveSecurity
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Help Net Security
Help Net Security
人人都是产品经理
人人都是产品经理
WordPress大学
WordPress大学
Cyberwarzone
Cyberwarzone
K
Kaspersky official blog
Security Latest
Security Latest
博客园 - 叶小钗
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
A
Arctic Wolf
C
Cisco Blogs
H
Heimdal Security Blog
雷峰网
雷峰网
阮一峰的网络日志
阮一峰的网络日志
Google DeepMind News
Google DeepMind News
小众软件
小众软件
T
Tenable Blog
Attack and Defense Labs
Attack and Defense Labs
N
News and Events Feed by Topic
The Last Watchdog
The Last Watchdog
V2EX - 技术
V2EX - 技术
Simon Willison's Weblog
Simon Willison's Weblog
Vercel News
Vercel News
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
V
Vulnerabilities – Threatpost
L
LangChain Blog
Y
Y Combinator Blog
V
V2EX
Hacker News - Newest:
Hacker News - Newest: "LLM"
Latest news
Latest news
D
Docker
AWS News Blog
AWS News Blog
Google Online Security Blog
Google Online Security Blog
H
Help Net Security
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
Troy Hunt's Blog
TaoSecurity Blog
TaoSecurity Blog
Cloudbric
Cloudbric
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC

yulingtianxia's blog

Flutter 官方终于出手了,DartNative 将何去何从? CPP-Summit 2020 实现 Native 异步回调 Flutter 如何实现 Flutter 同步调用 Native API DartNative 内存自动管理 如何实现一行命令自动生成 Flutter 插件 告别 Flutter Channel,调用 Native API 仅需一行代码! BlockHook and Memory Safety Passing Out Parameter in DartNative 在 Flutter 中玩转 Objective-C Block DartNative Struct DartNative Memory Management: C++ Non-Object DartNative Memory Management: NSObject 谈谈 dart_native 混合编程引擎的设计 用 Dart 来写 Objective-C 代码 App 二进制文件重排已经被玩坏了 BlockHook with Invocation(2) BlockHook with Private Data BlockHook with Revocation
BlockHook with Invocation(1)
杨萧玉 · 2019-07-27 · via yulingtianxia's blog

文章目录

  1. 1. 使用方法
  2. 2. 实现原理
    1. 2.1. BHInvocation 结构
    2. 2.2. retainArguments 实现
    3. 2.3. block_interceptor 实现
  3. 3. 后记

BlockHook 在业界已经率先解决了在同步调用场景下对 Objective-C Block 的 AOP 问题。但也有很多场景是需要先调用一段自己的逻辑,然后再异步延时执行 Block。

比如从外部跳转到 App 某个页面前需要检查下登录态,如果未登录则需要走完登录流程后才能继续跳转页面,而几乎所有基于 Block callback 的路由组件都没提供路由拦截器的功能。不同的路由组件内部实现不同,想要实现拦截器就需要针对不同的内部实现来修改路由组件源码。

因此我实现了 BlockHook 的异步拦截功能,所有基于 Block 的路由组件就都有了通用的路由拦截器!

当然,Block 拦截器的应用场景不仅于此。只要是需要『同步改异步执行』 Block 的场景都可以用到。

让子弹再飞一会儿!

使用方法

BlockHook 拦截器用法很简单,在已有 BHInvocation 参数的基础上,增加了一个 completion 回调。当拦截器的逻辑异步执行完后,调用 completion 即可继续执行原来的 Block。如果拦截器的逻辑是同步的,也依然可以用这个接口,只是没必要罢了,推荐直接用原来的 block_hookWithMode:usingBlock: 接口。

1
2
3
4
5
6
7
8
9
10
typedef void(^IntercepterCompletion)(void);








- (BHToken *)block_interceptor:(void (^)(BHInvocation *invocation, IntercepterCompletion completion))interceptor;

举个例子,拦截时修改传入的参数,并延迟 0.5 秒再执行 Block:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
NSObject *testArg = [NSObject new];
NSObject *testArg1 = [NSObject new];

NSObject *(^testblock)(NSObject *) = ^(NSObject *a) {
return [NSObject new];
};

[testblock block_interceptor:^(BHInvocation *invocation, IntercepterCompletion _Nonnull completion) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
*(void **)(invocation.args[1]) = (__bridge void *)(testArg1);
completion();
});
}];

testblock(testArg);

实现原理

首先想想如果要延迟一个 Objective-C 方法的执行,需要怎么做?

答案是利用消息转发机制,NSInvocation 调用 retainArguments 将方法执行所需的上下文持有,这样才能保证方法执行时所需的参数、target 等不会被释放。

对于 Block 来说,虽然也能通过 NSInvocation 来进行调用,但是经过 Hook 过后已经不再适用。因为 NSInvocation 的实现机制以及生命周期管理是个黑盒,且无法承载 Hook 相关的信息,需要自己来实现个 BHInvocation

BHInvocation 结构

我之前的 BlockHook with Struct 这篇文章提到了个技术点:在 x86 架构下,当 Block 返回值是大于 16 Byte 的 struct 时,参数列表有些变化:

为了兼容这种情况,需要两套 argsretValue。一套『真的』用于传给 libffi 调用原始函数指针,另一套『假的』提供给使用方读写参数和返回值。这样使用方无需关心底层特殊逻辑,直接用就行了。

BHInvocation 主要结构如下:

PS:BHInvocationNSInvocation 的场景和用法有些不同,所以实现上也会有差异。NSInvocation 没有公开源码,想了解原理的可以看看 mikeash 的实现: MAInvocation。但我并没有参考过 mikeash 的源码,因为等我写完了才发现它。。。

retainArguments 实现

retainArguments 实现策略:

  1. 拷贝 void **args 指针数组和返回值指针
  2. retain 指针内容类型为 Objective-C 对象的参数
  3. 如果参数中也有其他 Block 对象,则 copy 过来
  4. 如果参数中有 C-string,则 strcpy 过来

需要注意的是这里依然要考虑两套 argsretValue 的问题。代码就不贴了,有兴趣的可以自己去看。

block_interceptor 实现

解决了 retainArguments 的实现,一切都好说了。只要基于原有的 block_hookWithMode:usingBlock: 接口稍加改装即可:

1
2
3
4
5
6
7
8
9
10
11
- (BHToken *)block_interceptor:(void (^)(BHInvocation *invocation, IntercepterCompletion completion))interceptor {
return [self block_hookWithMode:BlockHookModeInstead usingBlock:^(BHInvocation *invocation) {
if (interceptor) {
IntercepterCompletion completion = ^() {
[invocation invokeOriginalBlock];
};
interceptor(invocation, completion);
[invocation retainArguments];
}
}];
}

后记

写了这么多关于 BlockHook 的文章,我越来越发现自己在苹果爸爸面前所表现出的无知。几乎每一步都要去踩很多坑,看很多源码。而这次是看着苹果爸爸的文档脑补如何实现,业界也没有能参考的先例。

这种感觉犹如自己在黑暗中不断探索,并享受着这种孤独。

标题暗示着,这篇文章可能会有后续的。