본문 바로가기
프로그래밍 놀이터/iOS

[Effective Objective-C] #46 dispatch_get_current_queue 사용을 피하라

by 돼지왕 왕돼지 2017. 10. 13.
반응형

 [Effective Objective-C] #46 dispatch_get_current_queue 사용을 피하라


출처 : Effective Objective-C

anti pattern, arc, corefoundation, deprecated, destructor function, dispatch_block_t, dispatch_function_t, dispatch_get_current_queue, dispatch_get_specific, dispatch_queue_create, dispatch_queue_set_specific, dispatch_queue_t, dispatch_set_target_queue, dispatch_sync, Foundation, GCD, global 병렬 큐, IOS, opaque void pointer, queue hierarchy, queue-specific data, sync 안 sync, toll-free bridged, void 포인터, [Effective Objective-C] #46 dispatch_get_current_queue 사용을 피하라, 값, 내부 동기화 큐, 데드락, 동기화 큐, 디버깅, 디스패치 큐 계층 구조, 메모리 관리, 무비용 전환, 불투명 void 포인터, 비교 검사, 소멸자 함수, 안티 패턴, 재진입, 직간접, 큐 계층, 큐 전용 데이터, 큐 지정 데이터, 키, 폐기


-

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 를 사용해야 하는 곳을 대체할 수 있다.

이를 사용하면 재진입 코드 때문에 발생하는 데드락을 회피할 수 있다.




반응형

댓글