美文网首页
dispatch_get_current_queue废弃原因

dispatch_get_current_queue废弃原因

作者: woshishui1243 | 来源:发表于2018-05-31 17:10 被阅读31次

    使用GCD的时候经常要判断当前代码是在哪个队列上执行的,会发现有下面这个函数:
    dispatch_queue_t dispatch_get_current_queue();
    iOS 6.0开始已经弃用这个函数了,它检测当前队列是不是某个特定的队列。

    - (NSString *)someString {  
         __block NSString *localSomeString;  
        dispatch_sync(_syncQueue, ^{  
          localSomeString = _someString;  
        });  
        return localSomeString;  
    }  
    
    - (void)setSomeString:(NSString *)someString{  
        dispatch_async(_syncQueue, ^{  
            _someString = someString;  
        });  
    }  
    

    获取方法可能会死锁。调用获取方法的队列恰好是同步操作所针对的队列(本例中是_syncQueue),dispatch_sync就一直不会返回,直到块执行完成。但是,执行块的目标队列是当前队列,当前队列的dispatch_sync之一阻塞,它在等目标队列把这个块执行完,块就永远没有机会执行了。
    可以检测当前队列是否为同步操作所针对的队列,如果是就不派发了,直接执行块。

    -(NSString *)someString {  
         __block NSString *localSomeString;  
        dispatch_block_t accessorBlock = ^{  
            localSomeString = _someString;  
        };  
        if (dispatch_get_current_queue() == _syncQueue) {  
            accessorBlock();  
        } 
        else {  
            dispatch_sync(_syncQueue, accessorBlock);  
        }  
        return localSomeString;  
    }  
    

    dispatch_get_current_queue方法可以处理一些简单的情况,下面这段代码有两个串行派发队列。

    - (void)demo1 {
        dispatch_queue_t queueA = dispatch_queue_create("com.dy.queueA", NULL);
        dispatch_queue_t queueB = dispatch_queue_create("com.dy.queueB", NULL);
        dispatch_sync(queueA, ^{
           dispatch_sync(queueB, ^{
                dispatch_sync(queueA, ^{
                   //......
                });
           });
        });
    }
    

    到最内层的派发队列时,会死锁。A在等B,B在等A,A阻塞。我们用dispatch_get_current_queue来检测。

    - (void)demo2 {
        dispatch_queue_t queueA = dispatch_queue_create("com.lyk.queueA", NULL);
        dispatch_queue_t queueB = dispatch_queue_create("com.lyk.queueB", NULL);
        
        dispatch_sync(queueA, ^{
            dispatch_sync(queueB, ^{
                dispatch_block_t block = ^{};
                if (dispatch_get_current_queue() == queueA) {
                    block();
                } else {
                    dispatch_sync(queueA, block);
                }
            });
       });
    }
    

    这样做依然死锁,dispatch_get_current_queue获取到的当前队列是queueB,所以结果依然执行针对queueA的同步派发操作。
    这个问题最好的解决办法是通过GCD所提供的功能来设定“队列特有数据”,可以把任意数据以键值对形式关联到队列中。如果根据指定的键获取不到关联数据,系统会沿着层级体系向上查找,直到找到数据或达到跟队列为止。

    -(void)demo3 {
        dispatch_queue_t queueA = dispatch_queue_create("com.lyk.queueA", NULL);   
        dispatch_queue_t queueB = dispatch_queue_create("com.lyk.queueB", NULL);   
        dispatch_set_target_queue(queueB, queueA);   
        static int kQueueSpecific;   
        CFStringRef queueSpecificValue = CFSTR("queueA");   
        dispatch_queue_set_specific(queueA, &kQueueSpecific, (voidvoid *)queueSpecificValue, (dispatch_function_t)CFRelease);   
        dispatch_sync(queueB, ^{   
            dispatch_block_t block = ^{   
            NSLog(@"NO deadlock!");   
        };   
        CFStringRef retrievedValue = dispatch_get_specific(&kQueueSpecific);   
        if (retrievedValue) {   
            block();   
        } else {   
            dispatch_sync(queueA, block);   
        }   
      });   
    }  
    

    创建了两个队列,队列B的目标队列是队列A,队列A的目标队列依然是全局并发队列。然后用dispatch_queue_set_specific,在队列A上设置键值。
    第一个参数是要设置数据的队列,第二个第三个参数是键和值,函数是按指针来比较键的,并不是按内容,这点和字典不同。值也是不透明的void指针,可以存放任意数据,但是必须管理该对象的内存。ARC环境下很难使用OC对象作为值。代码中使用CoreFoundation字符串作为值。
    函数的最后一个参数是析构函数,当队列所占内存被系统回收,或者有新的值与键相关联时,原有的值会被移除,析构函数在此时运行。

    typedef void (*dispatch_function_t)(void*)

    总结:
    1.dispatch_get_current_queue函数的行为常常与开发者所预期的不同。已废弃。
    2.由于派发队列是按层级组织的,无法单用某个队列对象来描述“当前队列”。
    3.dispatch_get_current_queue函数用于解决由不可重入的代码做引发的死锁,但是可以改用“队列特定数据”来解决。

    相关文章

      网友评论

          本文标题:dispatch_get_current_queue废弃原因

          本文链接:https://www.haomeiwen.com/subject/niwqsftx.html