태그 보관물: cocoa-touch

cocoa-touch

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

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

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



답변

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

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

** 편집하다 **

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

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

답변

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

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

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

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


답변

다른 답변은 훌륭하지만 저에게 대답은 구조적입니다. 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

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


답변

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

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

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

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

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

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

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


답변

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

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

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

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


답변

여전히 대기열 비교가 필요한 사람들을 위해 대기열을 레이블 또는 지정별로 비교할 수 있습니다. https://stackoverflow.com/a/23220741/1531141 확인


답변

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

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

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

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

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

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

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