【问题标题】:iOS Block Design: Nested Blocks With Return Values?iOS 块设计:带有返回值的嵌套块?
【发布时间】:2015-02-01 18:27:41
【问题描述】:

我有一个委托回调方法,需要将数据(例如 NSArray)返回给调用者。

我可以从另一个块调用中获取此数据。谁能帮我设计一个在这种情况下使用块的设计。

我想出了类似的方法,但不确定这是否是正确的方法。

NSArray* (^eventsForDate)(NSDate *) = ^(NSDate *date) {
    [[DataManager sharedInstance] getEventsForDate:date onSuccess:^(NSArray *events) {
        return events; //Obviously this doesn't work. Need help here.
    } onError:^(NSError *error) {
        return @[];
    }];
};

//Delegate call back
- (NSArray *) calendarEventsForDate:(NSDate *) date
{
    return eventsForDate(date);
}

【问题讨论】:

  • a coupleother questions 应该有帮助。你不能从一个块中返回任何东西,所以你只需要在完成处理程序中调用eventsForDate
  • 你当然可以从一个块中返回一些东西。它将返回给调用该块的任何人。在这种情况下,在 getEventsForDate: 内部的某个地方,我希望调用 onSuccess 或 onError 块,并且该调用将接收块返回的内容。问题是 getEventsForDate: 对结果有何影响。

标签: ios objective-c objective-c-blocks


【解决方案1】:

因此,您正在寻找的或多或少是以下内容。

NSArray* (^eventsForDate)(NSDate *) = ^NSArray*(NSDate *date) {
    __block NSArray *result = nil;
    [[DataManager sharedInstance] getEventsForDate:date onSuccess:^(NSArray *events) {
        result = events; // Line A
    } onError:^(NSError *error) {
        result = @[]; // Line B
    }];
    return result; // Line C
};

您不能在 Line ALine B 使用 return 的原因(请参考代码中的 cmets)是因为您返回的是 @987654324 @块函数和onError:块函数而不是eventsForDate:

但是,一个重要的注意事项:上面的设计只有在你的getEventsForDate: 没有dispatch 时才有效(或者它总是在eventsForData 块的同一个线程中运行。

编辑:

如上所述,如果您的getEventsForDate: 中有dispatch,这将不会总是按预期工作。因为您不能保证 Line C 将在 Line ALine B 之后执行。这真的归结为getEventsForDate: 的工作原理。这是另一个更好地说明问题的示例。如果代码底部没有sleep(1),您可能result 设为nil;使用sleep(1),您将可能获得@[@"A"]

- (void)getEventsForDate:(NSDate *)date
               onSuccess:(void(^)(NSArray *))sucessBlock
                 onError:(void(^)(NSError *))errorBlock
{
    dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(q, ^{
        sucessBlock(@[@"A"]);
    });
    // sleep(1);
}

所以你真正需要做的是阻止Line C 并等待至少Line ALine B 被执行。详情请参考这个问题:

How do I wait for an asynchronously dispatched block to finish?

简而言之,您可以编写如下内容:

NSArray* (^eventsForDate)(NSDate *) = ^NSArray*(NSDate *date) {
    dispatch_semaphore_t sema = dispatch_semaphore_create(0);

    __block NSArray *result = nil;
    [[DataManager sharedInstance] getEventsForDate:date onSuccess:^(NSArray *events) {
        result = events;
        dispatch_semaphore_signal(sema);
    } onError:^(NSError *error) {
        result = @[@"B"];
        dispatch_semaphore_signal(sema);
    }];

    dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
    return result;
};

编辑 2

但是,即使是上面的代码也可能对您有用。这听起来不是一个好主意。那是因为,如果getEventsForDate: 被分派到后台线程,这意味着它非常慢并且希望在后台执行。并用信号量或任何东西阻塞你的主线程会破坏一切。所以更好的方法仍然是在块中做任何你想做的事情,并永远删除return。类似于以下内容:

void (^eventsForDate)(NSDate *) = ^void(NSDate *date) {
    [[DataManager sharedInstance] getEventsForDate:date onSuccess:^(NSArray *events) {
        // Update UI and etc. 
    } onError:^(NSError *error) {
        // Update UI and etc. 
    }];
};

【讨论】:

  • 谢谢Yuchen...如何确保所有这些都在同一个线程上运行?或者有没有更好的设计可以尝试?
  • 好吧,这不能用一行来解释。给我几分钟。我会更新我的答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-05
  • 1970-01-01
  • 2020-09-25
  • 2013-02-28
  • 1970-01-01
相关资源
最近更新 更多