写高质量OC代码52建议总结:46.不要使用dispatch_get_current_queue

使用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.lyk.queueA", NULL);
    dispatch_queue_t queueB = dispatch_queue_create("com.lyk.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, (void *)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函数用于解决由不可重入的代码做引发的死锁,但是可以改用“队列特定数据”来解决。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值