我一直在阅读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语句?
答案 0 :(得分:3)
虽然直接的想法可能是让你的异步请求同步,但这很少是一个好主意,并且在segue中间这样做,例如这种情况,可能会有问题。尝试使异步方法同步几乎不是一个好主意。
而且,正如smyrgl指出的那样,"的概念不能仅仅从块中返回一个值"直观上很有吸引力,但是你可以定义自己的返回值的块(如Duncan指出的那样),你不能改变requestAccessToEntityType
的行为,以便它以这种方式返回一个值。它的异步模式中固有的是你必须对块内的授权状态进行操作,而不是在块之后。
所以,相反,我会建议重构这段代码。我建议您删除segue(可能是从"来自"场景中的控件启动)而不是尝试依赖shouldPerformSegueWithIdentifier
来确定segue是否可以作为调用此异步方法的结果。
相反,我会完全删除现有的segue并将其替换为IBAction
方法,该方法根据requestAccessToEntityType
的结果以编程方式启动segue。因此:
从按钮(或其他)中移除segue到下一个场景并删除此shouldPerformSegueWithIdentifier
方法;
在视图控制器本身之间创建一个新的segue(不是来自"来自"场景中的任何控件,而是在视图控制器本身之间)并为此segue提供一个故事板ID(例如,请参阅屏幕快照here或here);
将控件连接到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中,因为它会阻碍用户界面。你最好的办法是重新架构你正在做的事情,这样你就不会依赖正在完成的异步过程来继续。