[Effective Objective-C] #46 dispatch_get_current_queue 사용을 피하라
출처 : Effective Objective-C
-
GCD 를 사용하면서 특히 여러 큐에 디스패칭할 때 어떤 큐가 현재 실행되고 있는지 알아내야 하는 경우가 흔히 있다.
dispatch_queue_t dispatch_get_current_queue()
이 함수가 하는 일은 현재 실행되고 있는 큐를 반환하는 것이라고 문서에 나와 있다.
그러나 주의 깊게 다루어야 한다.
사실 이 함수는 iOS 6.0 에서 공식적으로 폐기되었다.
그러나 맥 OS X 10.8에서는 폐기되지 않았다.
그렇다 하더라도 맥 OS X 에서도 사용을 피해야 한다.
-
이 메서드를 흔히 사용하게 되는 전형적인 안티패턴(anti pattern)은 특정 큐를 동시에 디스패칭할 때 발생하는 데드락을 피하기 위해 현재 큐를 확인하는 것이다.
-
올바른 해결책은 접근자가 재진입이 필요하지 않게 만드는 것이다.
더 정확히 말하면 동기화에 사용된 큐가 절대로 간접적으로 동일한 큐를 다시 동기화 접근하여 사용되지 않는 것을 보장해야 한다.
-
큐는 계층적으로 이루어져 있다.
이는 한 큐에 넣어진 블록이 그 큐의 상위 큐 안에서 실행된다는 것을 뜻한다.
계층 구조 맨 위의 큐는 항상 global 병렬 큐 중 하나다.
-
큐가 계층적으로 되어 있기 때문에 동기적으로 디스패치될 큐와 현재 큐를 비교하는 검사는 항상 맞지는 않을 것이다.
-
콜백 블록에서 dispatch_get_current_queue 가 반환하는 큐가 항상 그것에게 주어진 큐와 동일할 것이라 생각하기 쉽다.
그러나 사실은 내부 동기화 큐가 대신 반환될 것이다.
-
이 문제를 해결하기 위해 가장 좋은 방법은 GCD 의 큐 지정 데이터(queue-specific data) 함수를 사용하는 것이다.
이 함수는 임의의 데이터를 큐에 키-값 쌍으로 연관시킬 수 있다.
dispatch_queue_t queueA = dispatch_queue_create(“queueA”, NULL);
dispatch_queue_t queueB = dispatch_queue_create(“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 retrivedValue = dispatch_get_specific(&kQueueSpecific);
if( retrivedValue ){
block();
} else{
dispatch_sync(queueA, block);
}
});
-
void dispatch_queue_set_specific(dispatch_queue_t queue, const void *key, void *context, dispatch_function_t destructor);
키와 값 둘 다 불투명한(opaque) void 포인터다.
키에 대해 반드시 알고 있어야 할 사항은 키는 내용이 아닌 포인터 값으로 비교된다는 것이다.
값(함수 프로토타입에서 context 라고 부르는)도 불투명한 void 포인터다.
그래서 여러분이 원하는 어떤 것도 될 수 있지만,
객체를 사용하길 원하면 그 객체에 대한 메모리 관리를 해줘야 한다.
이 때문에 ARC 를 사용하는 환경에서 값으로 오브젝티브-C 객체를 사용하기 매우 어려워진다.
ARC 는 CoreFoundation 객체의 메모리 관리는 신경 쓰지 않기 때문에,
그런 객체들이 큐 전용 데이터에 쓰기 좋다.
그 객체들은 필요하다면 오브젝티브-C Foundation 클래스로 무비용 전환(toll-free bridged)될 수 있기 때문이다.
함수의 마지막 인자는 주어진 키가 잡고 있는 객체가 제거될 때 실행되는 소멸자 함수(destructor function)이다.
-
큐 전용 데이터는 dispatch_get_current_queue 의 많이 알려진 위험성을 회피하기 위해 사용하기 쉽고 간단한 기능(mechanism) 을 제공한다.
-
dispatch_get_current_queue 가 자주 사용되는 또 다른 곳은 디버깅할 때다.
디버깅 용도로 이 메서드를 사용해도 괜찮다.
기억할 점
-
dispatch_get_current_queue 함수는 기대한 것처럼 동작하지 않는다.
이 함수는 폐기되었다.
그래서 디버깅할 때만 사용해야 한다.
-
디스패치 큐는 계층 구조로 구성되어 있다.
그렇기 때문에 현재 큐를 간단히 단일 큐 객체로 표현할 수 없다.
-
큐 전용 데이터는 dispatch_get_current_queue 를 사용해야 하는 곳을 대체할 수 있다.
이를 사용하면 재진입 코드 때문에 발생하는 데드락을 회피할 수 있다.
'프로그래밍 놀이터 > iOS' 카테고리의 다른 글
[Effective Objective-C] #48 반복문에는 블록 열거를 사용하라 (0) | 2017.10.15 |
---|---|
[Effective Objective-C] #47 시스템 프레임워크를 숙지하라 (0) | 2017.10.14 |
[ios] permission category (0) | 2017.10.12 |
[ios] 권장되는 permission guide (0) | 2017.10.11 |
[Effective Objective-C] #45 스레드 안전한 단일 시간 코드 실행은 dispatch_once 를 이용하라 (1) | 2017.10.10 |
댓글