从块中返回一个值

时间:2014-05-07 00:43:40

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

我一直在阅读Objectice-C块,因为我最近越来越多地遇到它们。我已经能够解决大多数异步块执行问题,但是我找到了一个我似乎无法修复的问题。我想为返回什么做__block BOOL,但我知道方法结束时的return语句将在块运行完成之前执行。我也知道我不能在块内返回一个值。

- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender {

    if ([identifier isEqualToString:@"Reminder Segue"]) {

        eventStore = [[EKEventStore alloc] init];

        [eventStore requestAccessToEntityType:EKEntityTypeReminder completion:^(BOOL granted, NSError *error) {

            if (!granted) {

                UIAlertView *remindersNotEnabledAlert;
                remindersNotEnabledAlert = [[UIAlertView alloc] initWithTitle:@"Reminders Not Enabled" message:@"In order for the watering reminder feature to function, please allow reminders for the app under the Privacy menu in the Settings app." delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];

            //I would like to put a simple return NO statement here, but I know it is impossible
            }
        }];
    }

    return YES;
}

如何从块创建简单的return语句?

3 个答案:

答案 0 :(得分:3)

虽然直接的想法可能是让你的异步请求同步,但这很少是一个好主意,并且在segue中间这样做,例如这种情况,可能会有问题。尝试使异步方法同步几乎不是一个好主意。

而且,正如smyrgl指出的那样,"的概念不能仅仅从块中返回一个值"直观上很有吸引力,但是你可以定义自己的返回值的块(如Duncan指出的那样),你不能改变requestAccessToEntityType的行为,以便它以这种方式返回一个值。它的异步模式中固有的是你必须对块内的授权状态进行操作,而不是在块之后。

所以,相反,我会建议重构这段代码。我建议您删除segue(可能是从"来自"场景中的控件启动)而不是尝试依赖shouldPerformSegueWithIdentifier来确定segue是否可以作为调用此异步方法的结果。

相反,我会完全删除现有的segue并将其替换为IBAction方法,该方法根据requestAccessToEntityType的结果以编程方式启动segue。因此:

  • 从按钮(或其他)中移除segue到下一个场景并删除此shouldPerformSegueWithIdentifier方法;

  • 在视图控制器本身之间创建一个新的segue(不是来自"来自"场景中的任何控件,而是在视图控制器本身之间)并为此segue提供一个故事板ID(例如,请参阅屏幕快照herehere);

  • 将控件连接到IBAction方法,在其中执行此requestAccessToEntityType,如果已授予,则会执行此segue,否则会显示相应的警告。

    因此,它可能看起来像:

    - (IBAction)didTouchUpInsideButton:(id)sender
    {
        eventStore = [[EKEventStore alloc] init];
    
        [eventStore requestAccessToEntityType:EKEntityTypeReminder completion:^(BOOL granted, NSError *error) {
    
            // by the way, this completion block is not run on the main queue, so
            // given that you want to do UI interaction, make sure to dispatch it
            // to the main queue
    
            dispatch_async(dispatch_get_main_queue(), ^{
                if (granted) {
                    [self performSegueWithIdentifier:kSegueToNextScreenIdentifier sender:self];
                } else {
                    UIAlertView *remindersNotEnabledAlert;
                    remindersNotEnabledAlert = [[UIAlertView alloc] initWithTitle:@"Reminders Not Enabled" message:@"In order for the watering reminder feature to function, please allow reminders for the app under the Privacy menu in the Settings app." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
                    [remindersNotEnabledAlert show];
                }
            });
        }];
    }
    

答案 1 :(得分:2)

您可以从块中返回值,就像从任何函数或方法中一样。但是,在异步方法上从完成块返回值没有意义。这是因为直到方法在稍后的某个日期运行之后才会调用块,到那时,没有地方可以返回结果。完成方法被异步调用。

为了使块返回一个值,您需要将块定义为返回值的类型,就像您必须定义一个返回值的方法一样。

块有点奇怪,如果未指定返回值,则假定返回值为空。

返回值的块的示例是NSArray方法indexOfObjectPassingTest中使用的块。该块的签名如下所示:

(BOOL (^)(id obj, NSUInteger idx, BOOL *stop))predicate

该块返回BOOL。它将一个对象,一个整数和一个指向BOOL的指针作为参数。使用此方法编写代码块时,会为数组中的每个对象重复调用代码,当您找到与您正在执行的测试匹配的对象时,将返回TRUE。

答案 2 :(得分:0)

如果你真的想让一个块同步(虽然我质疑这样做的有效性)你最好的选择是使用dispatch_semaphore。你可以这样做:

dispatch_semaphore_t mySemaphore = dispatch_semaphore_create(0);

__block BOOL success;

        [eventStore requestAccessToEntityType:EKEntityTypeReminder completion:^(BOOL granted, NSError *error) {
             success = granted;
             dispatch_semaphore_signal(mySemaphore);
        }];

dispatch_semaphore_wait(mySemaphore, DISPATCH_TIME_FOREVER);

然而,我再也不认为你想要这样做,尤其是在segue中,因为它会阻碍用户界面。你最好的办法是重新架构你正在做的事情,这样你就不会依赖正在完成的异步过程来继续。