2013-07-16 74 views
2

我有一種方法可以運行其他幾種方法。這些都有完成塊,我只想在我的主方法結束時返回一個值,只要我有每個子方法的結果。例如:只有準備就緒時才能返回方法?

-(NSMutableDictionary *)mainMethod 
{ 
    [self subMethod1Complete:^(NSMutableArray *results) 
    { 

    } 

    [self subMethod2Complete:^(NSMutableArray *results) 
    { 

    } 

    //return... 
} 

我只想在兩個子方法完成後返回字典。我怎樣才能做到這一點?

我確實有爲每種方法存儲BOOL的想法,所以我知道,不完整並且是完整的。所以當兩個都是YES時,我會返回我的字典。但我怎麼能夠按時並且不過早地打電話呢?

這是我無法解決的問題。希望你能幫忙,謝謝。

UPDATE:

我已經調整了我的代碼使用完畢塊,所以當我終於從其他方法的兩個其他完成塊接收數據,我跑最後一個編譯結果。下面你可以看到我的方法。你可以看到我的方法如下,到目前爲止還沒有成功,最終完成塊仍然過早地被調用。

對我來說很重要。 getTitlesgetThumbnails方法。在這些完成塊中,我獲得了我需要的數據。只有當我有這兩個這些,我想打電話給我的這個主要方法的最後完成塊。因此,一旦收到標題和縮略圖,它就會傳遞。

-(void)getFeedForUserID:(NSString *)channelID delegate:(id<YTHelperDelegate>)delegate complete:(void (^)(NSMutableDictionary * result))completionBlock properties:(NSString *)element, ... 
{ 
    va_list args; 
    va_start(args, element); 

    NSMutableArray *array = [NSMutableArray new]; 
    for (NSString *arg = element; arg != nil; arg = va_arg(args, NSString *)) [array addObject:arg]; 

    va_end(args); 

    NSMutableDictionary *resultsDict = [NSMutableDictionary new]; 

    dispatch_queue_t queue = dispatch_get_global_queue(0, 0); 
    dispatch_group_t group = dispatch_group_create(); 

    for (NSString *string in array) 
    { 
     if ([string isEqualToString:kFeedElementTitle]) 
     { 
      dispatch_group_async(group, queue, ^{ 
       [self getTitlesArrayForChannel:channelID completionHandler:^(NSMutableArray *results) { 
        dispatch_group_async(group, dispatch_get_main_queue(), ^{ 
         [resultsDict setObject:results forKey:kFeedElementTitle]; 
        }); 

       }]; 
      }); 
     } 
     if ([string isEqualToString:kFeedElementTitle]) 
     { 
      dispatch_group_async(group, queue, ^{ 
       [self getThumbnailsArrayForChannel:channelID completionHandler:^(NSMutableArray *results) { 
        dispatch_group_async(group, dispatch_get_main_queue(), ^{ 
         [resultsDict setObject:results forKey:kFeedElementThumbnail]; 
        }); 
       }]; 
      }); 
     } 
    } 

    dispatch_group_notify(group, dispatch_get_main_queue(), ^{ 
     completionBlock(resultsDict); 
    }); 
} 
+0

所以2種方法異步運行? – Wain

+0

假設這兩個塊都已完成,您無法從該方法返回 –

+0

更新了我的使用完成塊的方法,更合理,但仍然遇到了麻煩。不確定原因。 –

回答

2

您可以使用GCD和調度組功能。下面是解釋的文章吧:http://www.objc.io/issue-2/low-level-concurrency-apis.html#groups

例如,在你的情況,你的代碼可能是這個樣子了(文章無恥地複製和改編了一下)......

- (void)asyncMethod { 
    dispatch_group_t group = dispatch_group_create(); 

    dispatch_queue_t queue = dispatch_get_global_queue(0, 0); 
    dispatch_group_async(group, queue, ^(){ 
     NSMutableArray * results = [self subMethod1]; 
     dispatch_group_async(group, dispatch_get_main_queue(), ^(){ 
      self.subMethod1Results = results; 
     }); 
    }); 
    dispatch_group_async(group, queue, ^(){ 
     NSMutableArray * results = [self subMethod2]; 
     dispatch_group_async(group, dispatch_get_main_queue(), ^(){ 
      self.subMethod2Results = results; 
     }); 
    }); 

    // This block will run once everything above is done: 
    dispatch_group_notify(group, dispatch_get_main_queue(), ^(){ 
     // notify the app that both sets of data are ready 
     [self notifyWorkIsDone]; 
     // and release the dispatch group 
     dispatch_release(group); 
    }); 
} 

這需要一點點修改你的類的工作方式,因爲上面的方法是異步的(這是一件好事 - 當所有工作正在完成時,它不會阻止你的應用程序)。您需要的只是某種處理程序來調用並通知您的應用程序數據已準備就緒,您可以更新UI或進行任何其他必要的處理。

+1

正如另一位評論者所寫,您還可以使用dispatch_group_wait()等待該組完成。然後你可以把這個方法同步。調用dispatch_group_wait(group,DISPATCH_TIME_FOREVER);然後dispatch_release(group);之後進行清理。 –

+0

謝謝,儘管你的建議沒有運氣,但最終的完成塊仍然在這些方法完成之前被過早調用。檢查我的原始問題進行更新。 –

2

您在找GCD's dispatch_group的API。下面是來自蘋果的Concurrency Programming Guide一些示例代碼:

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 
dispatch_group_t group = dispatch_group_create(); 

// Add a task to the group 
dispatch_group_async(group, queue, ^{ 
    // Some asynchronous work 
}); 

// Do some other work while the tasks execute. 

// When you cannot make any more forward progress, 
// wait on the group to block the current thread. 
dispatch_group_wait(group, DISPATCH_TIME_FOREVER); 

// Release the group when it is no longer needed. 
dispatch_release(group); 

上更新代碼中的註釋:

你確定你的錯誤不在於你的第二個if語句檢查kFeedElementTitle第二次代替kFeedElementThumbnail我想想可能是你的意圖?


更新與工作示例:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 
{ 

    NSString *kFeedElementTitle = @"some"; 
    NSString *kFeedElementThumbnail = @"strings"; 
    NSArray *array = @[@"some", @"test", @"strings"]; 

    NSMutableDictionary *resultsDict = [NSMutableDictionary new]; 

    NSLog(@"App launched"); 

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 
    dispatch_group_t group = dispatch_group_create(); 

    for (NSString *string in array) 
    { 
     if ([string isEqualToString:kFeedElementTitle]) 
     { 
      dispatch_group_async(group, queue, ^{ 
       [NSThread sleepForTimeInterval:5]; // simulate network call 

       dispatch_group_async(group, dispatch_get_main_queue(), ^{ 
        [resultsDict setObject:@"title result" forKey:kFeedElementTitle]; 
        NSLog(@"Received title result"); 
       }); 
      }); 
     } 
     if ([string isEqualToString:kFeedElementThumbnail]) // Note: this was changed to kFeedElementThumbnail from kFeedElementTitle 
     { 
      dispatch_group_async(group, queue, ^{ 
       [NSThread sleepForTimeInterval:10]; // simulate network call 

       dispatch_group_async(group, dispatch_get_main_queue(), ^{ 
        [resultsDict setObject:@"thumbnail result" forKey:kFeedElementThumbnail]; 
        NSLog(@"Received thumbnail result"); 
       }); 
      }); 
     } 
    } 

    dispatch_group_notify(group, dispatch_get_main_queue(), ^{ 
     NSLog(@"final dictionary: %@", resultsDict); 
    }); 


    return YES; 
} 

輸出:

2013-07-16 21:02:46.468 d[947:a0b] App launched 
2013-07-16 21:02:51.471 d[947:a0b] Received title result 
2013-07-16 21:02:56.471 d[947:a0b] Received thumbnail result 
2013-07-16 21:02:56.472 d[947:a0b] final dictionary: { 
    some = "title result"; 
    strings = "thumbnail result"; 
} 
+0

在另一個答案中建議'dispatch_group_wait'超過'dispatch_group_notify'的任何好處?另外,GCD解決方案目前還沒有運氣,請檢查我的原始問題。 –

+0

它們基本上是等價的,但將'getFeedForUserID'的行爲更改爲同步或異步(分別),這可能很重要,具體取決於它調用的時間和位置。 – jszumski

+0

哎呀沒有注意到if語句的錯誤,但可悲的是,這不是問題。 –

-2

你不知道什麼時候該塊將返回所以你不會知道你是否有在數據如果我可以提出一個建議,你可以在這些塊中調用一個方法,該方法將檢查是否設置了這兩個字典,如果他們繼續進行,否則不要繼續進行

- (void)mainMethod 
{ 
    [self subMethod1Complete:^(NSMutableArray *results) 
    { 
     self.result1 = results; 
     [self method3]; 
    } 

    [self subMethod2Complete:^(NSMutableArray *results) 
    { 
     self.results2 = results; 
     [self method3]; 
    } 

} 

- (void)method3 { 
    if (self.results1 != nil && self.results2 != nil) { 
     [self startProcedure]; 
    } else { 
     // do nothing 
    } 

} 

雖然都在一起,我建議你重做代碼來做到這一點不同,僅僅是因爲你不能保證的一個塊將被返回的時間內完成,更何況他們兩人

你也可以做這樣的事情

-(NSMutableDictionary *)mainMethod 
{ 
    [self subMethod1Complete:^(NSMutableArray *results) 
    { 

    } 

    [self subMethod2Complete:^(NSMutableArray *results) 
    { 

    } 
    while(result == nil) 
     sleep(1); 

     //return... 
    } 

這又是非常糟糕的....這只是更好地重新編寫代碼

+1

這是相當可怕的建議...... – holex

+0

我用問題中提到的代碼回答了問題,我的觀點是給出一個答案,他可以利用他所擁有的,因此他不必重新編寫太多,我知道這是錯誤的,這就是爲什麼我建議重新編寫代碼.....雖然這不是最好的答案,有更好的選擇,我不真的覺得它值得投票值得考慮這些都是可以修復問題。這也是爲什麼我沒有在收到選票時刪除答案的原因。 –