iOS块设计:具有返回值的嵌套块?

时间:2015-02-01 18:27:41

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

我有一个委托回调方法,需要将数据(Ex 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);
}

1 个答案:

答案 0 :(得分:3)

所以你要找的或多或少是以下几点。

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
};

您无法在 A行 B行(请参阅代码中的注释)使用return的原因是您要返回onSuccess:阻止函数和onError:阻止函数而不是eventsForDate:

但是,重要提示:上述设计仅在getEventsForDate:dispatch时才有效(或者它始终在eventsForData块的同一个线程中运行。

修改:

如上所述,如果dispatch中有getEventsForDate:,则无法按预期工作。因为无法保证在 A行 B行之后执行 Line C 。这真的归结为getEventsForDate:的工作原理。这是另一个更好地说明问题的例子。如果代码底部没有sleep(1),您可能获取resultnil;使用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 并等到 A行 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:调度到后台线程,则意味着它非常慢并且希望在后台执行。用semephore阻止你的主线程或任何破坏一切的东西。因此,更好的方法仍然是在块中执行您想要执行的任何操作,并删除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. 
    }];
};