一、Dispatch Source
GCD 的一个特别有趣的特性是 Dispatch Source,它可以协调特殊的低等级系统事件,比如监测 Unix 信号、文件描述符、Mach 端口、VFS 节点,以及其它晦涩的东西。我们可以通过实现一个 Dispatch Source 对象并以一个相当奇特的方式来使用它来品尝那些晦涩的东西。
第一次使用 Dispatch Source 可能会迷失在如何使用一个源,所以你需要知晓的第一件事是 dispatch_source_create 如何工作。下面是创建一个源的函数原型:
dispatch_source_t dispatch_source_create(
dispatch_source_type_t type,
uintptr_t handle,
unsigned long mask,
dispatch_queue_t queue);
第一个参数是 dispatch_source_type_t
。这是最重要的参数,因为它决定了 handle 和 mask 参数将会是什么。你可以查看 Xcode 文档 得到哪些选项可用于每个 dispatch_source_type_t
参数。
下面你将监控 DISPATCH_SOURCE_TYPE_SIGNAL
。如文档所显示的:
一个监控当前进程信号的 Dispatch Source。 handle 是信号编号,mask 未使用(传 0 即可)。
这些 Unix 信号组成的列表可在头文件 signal.h 中找到。在其顶部有一堆 #define
语句。你将监控此信号列表中的 SIGSTOP
信号。这个信号将会在进程接收到一个无法回避的暂停指令时被发出。在你用 LLDB 调试器调试应用时你使用的也是这个信号。
在控制之中编写如下代码:
- (void)viewDidLoad {
[super viewDidLoad];
#if DEBUG // 1
dispatch_queue_t queue = dispatch_get_main_queue(); // 2
static dispatch_source_t source = nil; // 3
static dispatch_once_t onceToken;
__typeof(self) __weak weakSelf = self;
dispatch_once(&onceToken, ^{ // 4
source = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGSTOP, 0, queue); // 5
if (source) // 6
{
dispatch_source_set_event_handler(source, ^{ // 7
NSLog(@"received stop signal : %@", weakSelf); // 8
//exit(0);
});
dispatch_resume(source); // 9
}
});
#endif
}
这些代码有点儿复杂,所以跟着注释一步步走,看看到底发生了什么:
1.最好是在 DEBUG
模式下编译这些代码
2.你创建了一个dispatch_queue_t
实例变量而不是在参数上直接使用函数。当代码变长,分拆有助于增加可读性。
3.你需要 source
在方法范围之外也可被访问,所以你使用了一个 static
变量。
4.使用 dispatch_once
确保只会执行一次Dispatch Source
的设置。
5.初始化source
变量。你指明了你对信号监控感兴趣并提供了 SIGSTOP
信号作为第二个参数。进一步,你使用主队列处理接收到的事件——很快你就好发现为何要这样做。
6.如果你提供的参数不合法,那么 Dispatch Source
对象不会被创建。也就是说,在你开始在其上工作之前,你需要确保已有了一个有效的Dispatch Source
。
7.当你收到你所监控的信号时,dispatch_source_set_event_handler
就会执行。之后你可以在其Block
里执行合适的处理逻辑。
8.一个基本的NSLog
语句,它将对象打印到控制台。
9.默认的,所有源初始状态都为暂停。如果你要开始监控事件,你必须告诉源对象恢复到活跃状态。
编译并运行应用;在控制器中设置一个断点,查看控制台,你会看到dispatch_source_set_event_handler
函数确实可以工作。你看到的大概如下:
2019-05-24 15:10:59.594507+0800 GCDDemo[21232:1194608] received stop signal : <ViewController: 0x7fb2d4c0bb70>
2019-05-24 15:12:46.625728+0800 GCDDemo[21232:1194608] received stop signal : <ViewController: 0x7fb2d4c0bb70>
那么这有什么用?
1.利用上面的特性,可以做APP反调试的防护;
2.可以动态调试我们的应用;
反调试的防护:
当别人拿到我们的app进行逆向调试的时候,最重要的操作就是埋断点,或者去看页面的图形结构,而这些操作都会触发进程的中断信号,在你用 LLDB 调试器调试应用时你使用的也是这个信号。这些 Unix 信号组成的列表可在头文件 signal.h 中找到。我们可以使用dispatch_source来监控进程的中断信号,当信号到来时,dispatch_source_set_event_handler就会执行,这时在上面注释8的地方打开exit(0)的注释,就实现了简单的反调试;
动态调试我们的应用:
当你意外地停止调试器,你几乎从来都不会在所需的栈帧上。现在你可以在任何时候停止调试器并在你所需的地方执行代码。如果你想在你的应用的某一点执行的代码非常难以从调试器访问的话,这会非常有用.
将一个断点放在你刚添加在 viewDidLoad 里的事件处理器的 NSLog 语句上。调试器会直接暂停到这里,然后您就可以访问实例中你关心的内容了.使用这个方法,你可以更新 UI、查询类的属性,甚至是执行方法——所有这一切都不需要重启应用!
一起看一下效果:
GCDdemo.gif
参考@nixzhu在Github上的译文,
https://github.com/nixzhu/dev-blog/blob/master/2014-04-19-grand-central-dispatch-in-depth-part-1.md
原作者:Derek Selander
原文地:[http://www.raywenderlich.com/60749/grand-central-dispatch-in-depth-part-1]
网友评论