返回方法只在准备好后?

时间:2013-07-16 16:49:22

标签: ios objective-c methods block

我有一种方法可以运行其他几种方法。这些都有完成块,我只想在得到每个子方法的结果后,在main方法的末尾返回一个值。例如:

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

    }

    [self subMethod2Complete:^(NSMutableArray *results)
    {

    }

    //return...
}

一旦两个子方法完成,我只想在结尾处返回我的字典。我怎么能这样做?

我确实有为每种方法存储BOOL的想法,所以我知道,NO不完整,YES完成。所以当两者都是,我会回到我的字面。但我怎么能按时而不是过早地称呼呢?

更新

我已经调整了我的代码以使用完成块,所以当我最终从其他方法接收来自其他两个完成块的数据时,我运行最后一个带有编译结果。下面你可以看到我的方法。你可以在下面看到我的方法,到目前为止没有成功,最后的完成块仍然过早被调用。

对我来说很重要。 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);
    });
}

3 个答案:

答案 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 :(得分:2)

您正在寻找GCD的dispatch_group API。以下是Apple 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";
}

答案 2 :(得分:-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...
    }

这又是非常糟糕的......重写代码

更好