iOS 6의 완료 블록에 대한 dispatch_get_current_queue ()의 대안은 무엇입니까?


101

블록과 완료 블록을 받아들이는 메서드가 있습니다. 첫 번째 블록은 백그라운드에서 실행되어야하며 완료 블록은 메서드가 호출 된 모든 큐에서 실행되어야합니다.

후자의 경우 항상을 사용 dispatch_get_current_queue()했지만 iOS 6 이상에서는 더 이상 사용되지 않는 것 같습니다. 대신 무엇을 사용해야합니까?


dispatch_get_current_queue()iOS 6에서 지원이 중단 된 이유는 무엇입니까? 문서는 그것에 대해 아무 말도하지 않습니다
jere

3
컴파일러는 그것에 대해 불평합니다. 시도 해봐.
cfischer

4
헤더 파일을 확인 @jere, 그것은이 depricated되는 상태를 않습니다
WDUK

모범 사례에 대한 토론 외에도 질문에 답할 수있는 [NSOperationQueue currentQueue]가 있습니다. 사용에 대한주의 사항에 대해 잘 모르겠습니다.
Matt

경고 발견 ------ [NSOperationQueue currentQueue] dispatch_get_current_queue ()와 동일하지 않음 ----- 때때로 null을 반환합니다. ---- dispatch_async (dispatch_get_global_queue (0, 0), ^ {NSLog (@ "q (0, 0)은 % @ ", dispatch_get_current_queue ()); NSLog (@"cq (0,0)은 % @ ", [NSOperationQueue currentQueue]);}); ----- q (0,0)은 <OS_dispatch_queue_root : com.apple.root.default-qos [0x100195140]> cq (0,0)은 (null) ----- 사용이 중단되었거나 dispatch_get_current_queue ()가 아닌 것 같습니다 나는 모든 조건에서 현재 큐를보고 볼 수있는 유일한 해결책이 될 수 있습니다
고질라를

답변:


64

"발신자가 어떤 대기열에서든 실행"하는 패턴은 매력적이지만 궁극적으로 좋은 생각은 아닙니다. 해당 대기열은 우선 순위가 낮은 대기열, 기본 대기열 또는 이상한 속성이있는 다른 대기열 일 수 있습니다.

제가 가장 좋아하는 접근 방식은 "완료 블록이 x, y, z 속성을 가진 구현 정의 큐에서 실행됩니다"라고 말하고 호출자가 그보다 더 많은 제어를 원할 경우 블록이 특정 큐로 디스패치하도록하는 것입니다. 지정할 일반적인 속성 집합은 "다른 응용 프로그램에서 볼 수있는 큐와 관련하여 직렬, 재진입 및 비동기"와 같은 것입니다.

** 편집하다 **

Catfish_Man은 아래 의견에 예를 들어 답변에 추가하고 있습니다.

- (void) aMethodWithCompletionBlock:(dispatch_block_t)completionHandler     
{ 
    dispatch_async(self.workQueue, ^{ 
        [self doSomeWork]; 
        dispatch_async(self.callbackQueue, completionHandler); 
    } 
}

7
전적으로 동의 한. Apple은 항상 이것을 따르고 있음을 알 수 있습니다. 당신이 메인 큐에서 무언가를하고 싶을 때마다 당신은 항상 메인 큐로 디스패치해야합니다. 애플은 항상 당신이 다른 스레드에 있다는 것을 보장하기 때문입니다. 대부분의 경우 장기 실행 프로세스가 데이터 가져 오기 / 조작을 완료 할 때까지 기다린 다음 완료 블록에서 백그라운드에서 바로 처리 한 다음 UI 호출 만 기본 큐의 디스패치 블록에 고정 할 수 있습니다. 또한 개발자가 패턴에 익숙해지기 때문에 Apple이 기대하는 바를 따르는 것이 항상 좋습니다.
Jack Lawrence

1
훌륭한 대답 .. 그러나 나는 당신이 말하는 것을 설명하기 위해 적어도 몇 가지 샘플 코드를 바라고 있었다
abbood

3
- (무효) aMethodWithCompletionBlock (dispatch_block_t) completionHandler {dispatch_async (self.workQueue ^ {[자기 doSomeWork] dispatch_async (self.callbackQueue, completionHandler);}}
Catfish_Man

(완전히 간단한 예)
Catfish_Man 2013 년

3
dispatch_sync () 및 dispatch_set_target_queue ()로 인해 동시에 둘 이상의 대기열에있을 수 있기 때문에 일반적인 경우에는 불가능합니다 (실제로는 상당히 가능성이 높습니다). 가능한 일반적인 경우의 일부 하위 집합이 있습니다.
Catfish_Man

27

이것은 기본적으로 설명하는 API에 대한 잘못된 접근 방식입니다. API가 실행할 블록 및 완료 블록을 수락하는 경우 다음 사실이 참이어야합니다.

  1. "실행 차단"은 내부 대기열에서 실행되어야합니다. 예를 들어 API에 대해 비공개이므로 해당 API의 제어를 완전히받는 대기열입니다. 이에 대한 유일한 예외는 API가 블록이 기본 대기열 또는 전역 동시 대기열 중 하나에서 실행될 것이라고 구체적으로 선언하는 경우입니다.

  2. 완료 블록은 # 1과 동일한 가정이 참인 경우가 아니면 항상 튜플 (대기열, 블록)로 표현 되어야 합니다. 예를 들어, 완료 블록은 알려진 글로벌 큐에서 실행됩니다. 또한 완료 블록은 전달 된 대기열에서 비동기로 전달되어야합니다.

이것은 단순한 스타일 포인트가 아니라, API가 교착 상태 또는 언젠가는 가장 가까운 트리에 매달릴 다른 엣지 케이스 동작으로부터 안전하려면 전적으로 필요합니다. :-)


11
합리적으로 들리지만 어떤 이유로 든 자체 API에 대해 Apple이 취한 접근 방식이 아닙니다. 완료 블록을 사용하는 대부분의 메소드는 대기열도 사용하지 않습니다 ...
cfischer 2011

2
사실이며, 완료 블록이 주 대기열 또는 전역 동시 대기열에서 실행된다는 것이 명백한 경우 이전 어설 션을 다소 수정합니다. 나는 많은 것을 나타 내기 위해 내 대답을 바꿀 것입니다.
jkh

애플이 이러한 접근 방식을 취하지 않는다는 점에 대해 언급하자면, 애플이 정의에 따라 항상 "옳은"것은 아닙니다. 적절한 주장은 과학자가 확인할 특정 권위보다 항상 강력합니다. 위의 대답은 적절한 소프트웨어 아키텍처 관점에서 매우 잘 설명한다고 생각합니다.
Werner Altewischer

14

다른 답변은 훌륭하지만 저에게 대답은 구조적입니다. Singleton에 다음과 같은 메서드가 있습니다.

- (void) dispatchOnHighPriorityNonMainQueue:(simplest_block)block forceAsync:(BOOL)forceAsync {
    if (forceAsync || [NSThread isMainThread])
        dispatch_async_on_high_priority_queue(block);
    else
        block();
}

두 가지 종속성이 있습니다.

static void dispatch_async_on_high_priority_queue(dispatch_block_t block) {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), block);
}

typedef void (^simplest_block)(void); // also could use dispatch_block_t

그런 식으로 다른 스레드에서 디스패치하도록 호출을 중앙 집중화합니다.


12

dispatch_get_current_queue우선 사용에주의해야합니다 . 헤더 파일에서 :

디버깅 및 로깅 목적으로 만 권장됩니다.

코드는 전역 대기열 중 하나이거나 코드가 자체적으로 생성 한 대기열이 아니라면 반환 된 대기열에 대해 어떠한 가정도해서는 안됩니다. 코드는 해당 큐가 dispatch_get_current_queue ()에 의해 반환 된 큐가 아닌 경우 큐에 대한 동기 실행이 교착 상태로부터 안전하다고 가정해서는 안됩니다.

다음 두 가지 중 하나를 수행 할 수 있습니다.

  1. 원래 게시 한 대기열에 대한 참조를 유지하고 (을 통해 만든 경우 dispatch_queue_create) 그때부터 사용합니다.

  2. 을 통해 시스템 정의 대기열을 사용하고 사용중인 대기열을 dispatch_get_global_queue추적합니다.

이전에는 시스템에 의존하여 현재 대기중인 대기열을 추적하는 동안 효과적으로 작업을 수행해야합니다.


16
dispatch_get_current_queue()어떤 대기열인지 알아내는 데 사용할 수없는 경우 "원래 게시 한 대기열에 대한 참조를 유지"하려면 어떻게해야 합니까? 어떤 큐에서 실행 중인지 알아야하는 코드가 제어하거나 인식하지 못하는 경우가 있습니다. 백그라운드 큐에서 실행할 수 있고 실행해야하는 많은 코드가 있지만 가끔 GUI (진행률 표시 줄 등)를 업데이트해야하므로 해당 작업을 위해 주 큐로 dispatch_sync ()해야합니다. 이미 메인 큐에 있다면 dispatch_sync ()는 영원히 잠 깁니다. 이를 위해 내 코드를 리팩토링하는 데 몇 달이 걸립니다.
Abhi Beckert

3
NSURLConnection이 호출 된 동일한 스레드에서 완료 콜백을 제공한다고 생각합니다. 동일한 API "dispatch_get_current_queue"를 사용하여 콜백시 사용하기 위해 호출 된 대기열을 저장합니까?
defactodeity

5

Apple은 더 이상 사용하지 dispatch_get_current_queue()않지만 다른 곳에 구멍을 남겼으므로 현재 디스패치 대기열을 계속 가져올 수 있습니다.

if let currentDispatch = OperationQueue.current?.underlyingQueue {
    print(currentDispatch)
    // Do stuff
}

이것은 적어도 메인 대기열에서 작동합니다. 이 underlyingQueue속성은 iOS 8부터 사용할 수 있습니다.

원본 대기열에서 완료 블록을 수행해야하는 경우 OperationQueueGCD없이 직접 사용할 수도 있습니다 .



0

이것은 나도 대답입니다. 그래서 우리의 사용 사례에 대해 이야기하겠습니다.

서비스 계층과 UI 계층 (다른 계층 중에서)이 있습니다. 서비스 계층은 백그라운드에서 작업을 실행합니다. (데이터 조작 작업, CoreData 작업, 네트워크 호출 등). 서비스 계층에는 UI 계층의 요구 사항을 충족하기 위해 몇 가지 작업 대기열이 있습니다.

UI 계층은 서비스 계층에 의존하여 작업을 수행 한 다음 성공 완료 블록을 실행합니다. 이 블록에는 UIKit 코드가 포함될 수 있습니다. 간단한 사용 사례는 서버에서 모든 메시지를 가져오고 컬렉션 뷰를 다시로드하는 것입니다.

여기서 우리는 서비스 계층으로 전달되는 블록이 서비스가 호출 된 대기열에서 발송된다는 것을 보장합니다. dispatch_get_current_queue는 더 이상 사용되지 않는 메서드이므로 NSOperationQueue.currentQueue를 사용하여 호출자의 현재 대기열을 가져옵니다. 이 속성에 대한 중요 사항.

실행중인 작업의 컨텍스트 외부에서이 메서드를 호출하면 일반적으로 nil이 반환됩니다.

우리는 항상 알려진 대기열 (사용자 정의 대기열 및 기본 대기열)에서 서비스를 호출하기 때문에 이것은 우리에게 잘 작동합니다. serviceA가 serviceC를 호출 할 수있는 serviceB를 호출 할 수있는 경우가 있습니다. 첫 번째 서비스 호출이 발생하는 위치를 제어하므로 나머지 서비스도 동일한 규칙을 따릅니다.

따라서 NSOperationQueue.currentQueue는 항상 대기열 중 하나 또는 MainQueue를 반환합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.