内容简介:这些问题也是开发者在使用时向我反馈过的问题,在这篇文章里,这些问题都将会解决。关于
BlockHook 开创性地解决了 Objective-C 语言界 Hook Block 的问题,但也迎来了新的问题:
- 如何知道某个 Block 对象被谁 Hook 过?
- 多次 Hook 的先后顺序?
- 如何处理好多次 Hook 同一个 Block 后对任意一次 Hook 的撤销?
这些问题也是开发者在使用时向我反馈过的问题,在这篇文章里,这些问题都将会解决。
关于 BlockHook 的原理,可以先阅读之前的文章:
按顺序构造『虚拟的』 Hook 链表
首先要有一个链表来按先后顺序记录一个 Block 对象上所有的 Hook。这个链表的格式以及持有关系也需要考虑在内。
为此我构造了一个虚拟的链表来记录 Hook 的先后关系,而不是单独创建一个链表显式的记录。首先介绍下 Block 与 token 之间的引用关系:
可以看出每个 BHToken
记录了原始和替换后的 invoke
函数指针,那么先后两次 Hook 就靠 invoke
函数指针来关联了: 每个 tokne 的 originalInvoke
就是上一次 Hook 的 token 的 replacementInvoke
。而拿到 token 又是靠 Block 对象上的 AssociatedObject
,且 key 为 replacementInvoke
。这样就构造了一条虚拟的链表:想要获得上次 Hook 的 token,只需在 Hook 的 Block 对象上使用 originalInvoke
作为 key 即可。
下面的代码展示了获取下个 token 的 next
实现。因为链表可能会有新的插入和删除节点操作,所以需确保线程安全。
- (BHToken *)next { BHLock *lock = [self.block bh_lockForKey:@selector(next)]; [lock lock]; if (!_next) { _next = objc_getAssociatedObject(self.block, self.originInvoke); } BHToken *result = _next; [lock unlock]; return result; }
这里使用 replacementInvoke
来作为 AssociatedObject
的 key 真是 一举多得 :
invoke
下面的代码展示了如何获取最后一次 Hook 的 token。在读取 invoke
函数指针的时候,注意保证线程安全。
- (BHToken *)block_currentHookToken { if (![self block_checkValid]) { return nil; } void *invoke = [self block_currentInvokeFunction]; return objc_getAssociatedObject(self, invoke); } - (void *)block_currentInvokeFunction { struct _BHBlock *bh_block = (__bridge void *)self; BHLock *lock = [self bh_lockForKey:_cmd]; [lock lock]; void *invoke = bh_block->invoke; [lock unlock]; return invoke; }
撤销 Hook
俗话说『请神容易送神难』。好多 Hook 框架只解决的如何 Hook,但是却无法撤销恢复原样,留下一堆烂摊子。
在搭建了 Hook 链表的基础上,多次 Hook 的链表可以简化成 invoke
函数指针之间的关系:
那么撤销 Hook 就可以从链表头部开始遍历,找到当前要 remove
的 token。接着链表上删除这个 token,而这又可以分为两个子问题:
- 移除最后一次 Hook:需要将 Block 的
invoke
指针指向 token 的originalInvoke
。 - 移除 非 最后一次 Hook:需要将上一次 Hook token 的
originalInvoke
指向当前 token 的originalInvoke
。
最后肯定还要解除 Block 对象对 token 的持有。
这部分逻辑的实现代码如下,在操作 Block 的 invoke
指针时依然需要注意线程安全问题:
- (BOOL)remove { if (self.isStackBlock) { NSLog(@"Can't remove token for StackBlock!"); return NO; } self.deadBlock = nil; if (self.originInvoke) { if (self.block) { BHToken *current = [self.block block_currentHookToken]; BHToken *last = nil; while (current) { if (current == self) { if (last) { // remove middle token last.originInvoke = self.originInvoke; last.next = nil; } else { // remove head(current) token BHLock *lock = [self.block bh_lockForKey:@selector(block_currentInvokeFunction)]; [lock lock]; ((__bridge struct _BHBlock *)self.block)->invoke = self.originInvoke; [lock unlock]; } break; } last = current; current = [current next]; } } self.originInvoke = NULL; objc_setAssociatedObject(self.block, _replacementInvoke, nil, OBJC_ASSOCIATION_RETAIN); return YES; } return NO; }
以上所述就是小编给大家介绍的《BlockHook with Revocation》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
高可用架构(第1卷)
高可用架构社区 / 电子工业出版社 / 2017-11-1 / 108.00元
《高可用架构(第1卷)》由数十位一线架构师的实践与经验凝结而成,选材兼顾技术性、前瞻性与专业深度。各技术焦点,均由极具代表性的领域专家或实践先行者撰文深度剖析,共同组成“高可用”的全局视野与领先高度,内容包括精华案例、分布式原理、电商架构等热门专题,及云计算、容器、运维、大数据、安全等重点方向。不仅架构师可以从中受益,其他IT、互联网技术从业者同样可以得到提升。一起来看看 《高可用架构(第1卷)》 这本书的介绍吧!