iOS学习笔记5-GCDAsyncUdpSocket中的dispatch_queue_set_specific

时间:2022-03-09 17:36:26

用到GCDAsyncUdpSocket开源库的时候,发现了这个函数:
dispatch_queue_set_specific,于是做下笔记:

可重入的概念

首先,可重入的概念有点类似递归,一个函数,如果能够并行进入该函数且不出现问题,则可为可重入,而GCD是一个对共享资源的访问进行串行化的队列,所以是不可重入的,需要寻找一些技巧去绕开这个限制;

GCD可重入方法(2种方法):

1.dispatch_get_current_queue,但是这种方法已经不被苹果推荐了;
具体参考:被废弃的dispatch_get_current_queue
2.利用dispatch_queue_set_specific
首先参考被废弃的dispatch_get_current_queue中的一段代码:

dispatch_queue_t queueA = dispatch_queue_create("com.yiyaaixuexi.queueA", NULL);  
dispatch_queue_t queueB = dispatch_queue_create("com.yiyaaixuexi.queueB", NULL);
dispatch_set_target_queue(queueB, queueA);

static int specificKey;
CFStringRef specificValue = CFSTR("queueA");
dispatch_queue_set_specific(queueA,
&specificKey,
(void*)specificValue,
(dispatch_function_t)CFRelease);

dispatch_sync(queueB, ^{
dispatch_block_t block = ^{
//do something
};
CFStringRef retrievedValue = dispatch_get_specific(&specificKey);
if (retrievedValue) {
block();
} else {
dispatch_sync(queueA, block);
}
});

而在开源库GCDAsyncUdpSocket中,也使用了类似的方法:

        // The dispatch_queue_set_specific() and dispatch_get_specific() functions take a "void *key" parameter.
// From the documentation:
//
// > Keys are only compared as pointers and are never dereferenced.
// > Thus, you can use a pointer to a static variable for a specific subsystem or
// > any other value that allows you to identify the value uniquely.
//
// We're just going to use the memory address of an ivar.
// Specifically an ivar that is explicitly named for our purpose to make the code more readable.
//
// However, it feels tedious (and less readable) to include the "&" all the time:
// dispatch_get_specific(&IsOnSocketQueueOrTargetQueueKey)
//
// So we're going to make it so it doesn't matter if we use the '&' or not,
// by assigning the value of the ivar to the address of the ivar.
// Thus: IsOnSocketQueueOrTargetQueueKey == &IsOnSocketQueueOrTargetQueueKey;

IsOnSocketQueueOrTargetQueueKey = &IsOnSocketQueueOrTargetQueueKey;//这里用地址等于地址的地址,英文有解释的

void *nonNullUnusedPointer = (__bridge void *)self;
dispatch_queue_set_specific(socketQueue, IsOnSocketQueueOrTargetQueueKey, nonNullUnusedPointer, NULL);
//GCD本身是不可重入的,当设置了这一条的时候,根据IsOnSocketQueueOrTargetQueueKey就可以在下面进行相关操作了。

而在接下来的代码中使用如下:

    if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey))
block();
else
dispatch_async(socketQueue, block);

block()是块代码;
通过以上代码就能够让GCDAsyncUdpSocket实现可重入,这应该也能够解释我发送或者接收几乎是同时的缘故了。