2012-10-27 35 views
26

我已經看到一些相關的問題,但似乎沒有人回答這種情況。我想寫一個方法,將在後臺做一些工作。我需要這種方法來調用與原始方法調用相同的線程/隊列的完成回調。dispatch_async並在原隊列中調用完成處理程序

- (void)someMethod:(void (^)(BOOL result))completionHandler { 
    dispatch_queue_t current_queue = // ??? 

    // some setup code here 
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 
     BOOL ok = // some result 

     // do some long running processing here 

     dispatch_async(current_queue, ^{ 
      completionHandler(ok); 
     }); 
    }); 

這裏需要什麼樣的魔法咒語,所以完成處理程序被調用在同一個隊列或線程中調用sameMethod?我不想承擔主線。當然dispatch_get_current_queue不會被使用。

+0

你能描述你想要達到什麼樣的?爲什麼你的特定目的對它執行的線程有影響? –

+0

@ChristopherPickslay'someMethod'可能會在某個後臺線程中調用。我希望它可以在同一個線程上調用完成塊,而不是主線程或其他任意後臺線程。 – rmaddy

+0

我明白這一點。問題是爲什麼。是否有某些技術原因需要在特定線程上調用?我只是想,可能會有不同的設計,這將有所幫助。 –

回答

4

你不能真的使用隊列,因爲除了主隊列之外,他們都不能保證在任何特定的線程上運行。相反,你必須得到線程並直接在那裏執行你的塊。

Mike Ash's Block Additions適應:

// The last public superclass of Blocks is NSObject 
@implementation NSObject (rmaddy_CompletionHandler) 

- (void)rmaddy_callBlockWithBOOL: (NSNumber *)b 
{ 
    BOOL ok = [b boolValue]; 
    void (^completionHandler)(BOOL result) = (id)self; 
    completionHandler(ok); 
} 

@end 

- (void)someMethod:(void (^)(BOOL result))completionHandler { 
    NSThread * origThread = [NSThread currentThread]; 

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 
     BOOL ok = // some result 

     // do some long running processing here 

     // Check that there was not a nil handler passed. 
     if(completionHandler){ 
      // This assumes ARC. If no ARC, copy and autorelease the Block. 
      [completionHandler performSelector:@selector(rmaddy_callBlockWithBOOL:) 
             onThread:origThread 
            withObject:@(ok) // or [NSNumber numberWithBool:ok] 
           waitUntilDone:NO]; 
     } 
     }); 
    }); 

雖然你不使用dispatch_async(),這仍是異步相對於你的程序的其餘部分,因爲它包含了原派遣任務中塊,並waitUntilDone:NO也使它異步就此。

+0

這是一個有趣的想法。但是這有一個嚴重的缺點。對於我想要以這種方式使用的每個可能的塊簽名,我將不得不添加相應的類別方法。 – rmaddy

+0

**「除了主隊列之外,它們都不能保證在任何特定的線程上運行」** - 您能否指出我在蘋果文檔中提到的這個地方? – Macondo2Seattle

+0

@BlackRider:https://developer.apple.com/library/mac/documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html,位於「調度隊列任務」標題下。 –

11

如果您查看Apple文檔,看起來有兩種模式。

如果假定完成處理程序要在主線程上運行,則不需要提供隊列。一個例子是UIViewanimations方法:

+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion 

否則,API通常會詢問主叫方提供一個隊列:

[foo doSomethingWithCompletion:completion targetQueue:yourQueue]; 

我的建議是遵循這個模式。如果不清楚應該調用完成處理程序的哪個隊列,則調用程序應該明確地將其作爲參數提供。

+0

查看'UIDocument saveToURL:forSaveOperation:completionHandler:'的文檔。完成處理程序狀態的描述*在調用隊列中調用此塊。這是我希望達到的。 – rmaddy

+0

+1創建您自己的串行隊列並運行該隊列上的方法和完成塊。 – Abizern

+0

我認爲這是UIDocument中的一個錯誤。它不應該假定始發隊列具有它想要的行爲。 –

2

不知道這是否就能解決問題,但關於使用NSOperations而不是GCD?:

- (void)someMethod:(void (^)(BOOL result))completionHandler { 
NSOperationQueue *current_queue = [NSOperationQueue currentQueue]; 

// some setup code here 
NSOperationQueue *q = [[NSOperationQueue alloc] init]; 
[q addOperationWithBlock:^{ 
    BOOL ok = YES;// some result 

    // do some long running processing here 
    [current_queue addOperationWithBlock:^{ 
     completionHandler(ok); 
    }]; 
}]; 
+1

[NSOperationQueue currentQueue]可能會返回零。 「從運行操作的上下文外調用此方法通常會導致返回零。」 – BB9z

0

我想要做一些任務上的一些隊列,然後執行完成塊作爲@rmaddy提到如何。我遇到了併發編程指南,從蘋果和實施這個(與dispatch_retain & dispatch_released註釋掉了,因爲我使用ARC) - https://developer.apple.com/library/ios/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html#//apple_ref/doc/uid/TP40008091-CH102-SW1

void average_async(int *data, size_t len, dispatch_queue_t queue, void (^block)(int)) 
{ 
// Retain the queue provided by the user to make 
// sure it does not disappear before the completion 
// block can be called. 
//dispatch_retain(queue); // comment out if use ARC 

// Do the work on user-provided queue 
dispatch_async(queue, ^{ 
    int avg = average(data, len); 
    dispatch_async(queue, ^{ block(avg);}); 

    // Release the user-provided queue when done 
    //dispatch_release(queue); // comment out if use ARC 
}); 
} 
+0

你用錯誤的方式引用了答案:)第一個異步是在全局隊列中。在該隊列上完成工作後,該塊將在傳入的隊列上執行 – hariszaman

相關問題