强制一个方法等待另一个完成

在我到达目的地视图控制器之前,我原来的视图控制器,我调用一个方法,获取我的关键参数,然后我在下面的方法中设置我的目标视图控制器中的关键参数。 但是,关键参数在doSomethingToGetKey方法完成之前设置,所以传递一个空值。 我想知道是否有办法让第二种方法等待第一种方法完成。

起源视图控制器:

 -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{ [self doSomethingToGetKey]; [segue.destinationViewController setKey:_key]; } -(void)doSomethingToGetKey:(ACAccount *)account{ [_apiObject postReverseOAuthTokenRequest:^(NSString *authenticationHeader) { APIObject *api = [APIObject APIOSWithAccount:account]; [api verifyCredentialsWithSuccessBlock:^(NSString *username) { [api postReverseAuthAccessTokenWithAuthenticationHeader:authenticationHeader successBlock:^(NSString *oAuthToken, NSString *oAuthTokenSecret, NSString *userID, NSString *screenName) { _key = oAuthToken; _secret = oAuthTokenSecret; _userId = userID; } errorBlock:^(NSError *error) { NSLog(@"ERROR 1, %@", [error localizedDescription]); exit(1); }]; } errorBlock:^(NSError *error) { NSLog(@"ERROR 2: %@",error.description); exit(1); }]; } errorBlock:^(NSError *error) { NSLog(@"ERROR 3: %@",error.description); exit(1); }]; }; 

通常这种模式涉及使用一个完成块,例如:

 - (void) doSomethingToGetKeyWithCompletionHandler:(void (^)(NSString *key))completion { // perform asynchronous request // in its completion handler, call the `completion` block parameter given above [api doingSomeOtherAsynchronousMethodWithCompletion:^{ completion(key); }]; } 

然后,而不是:

 [self doSomethingToGetKey]; [self doSomethingElseWithKey:_key]; 

你可以这样做:

 [self doSomethingToGetKeyWithCompletionHandler:^(NSString *key){ [self doSomethingElseWithKey:key]; }]; 

但是,在这种情况下,你正在尝试在prepareForSegue完成所有这些。 这完全改变了这个问题,因为在这个asynchronous方法被调用之前,segue仍然会被执行,从而破坏了这个完成块模式的目的。

因此,你也想改变你的button(或其他),而不是执行segue本身,而是调用一个IBAction ,然后让程序执行segue( 如图所示 )。 因此,你最终会得到一个IBAction如:

 - (IBAction)tappedButton:(id)sender { // perhaps update UI so user knows something slow is happening first, // eg, show a `UIActivityIndicatorView` [self doSomethingToGetKeyWithCompletionHandler:^(NSString *key){ // remove that `UIActivityIndicatorView`, if you showed one [self performSegueWithIdentifier:@"Details" sender:self]; }]; } - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { if ([segue.identifier isEqualToString:@"Details"]) { [segue.destinationViewController setKey:_key] } } 

或者,您可以删除所有这些,只执行segue,但然后让目标视图控制器呈现自己,显示活动指示器视图,执行api调用,并删除活动指示器视图。 这是对代码更重大的重构,但可能是更好的用户体验。

但是,从prepareForSegue底线来看,你不能做一些asynchronous的事情,并且期望目标视图控制器等待它,即使你使用了完成块模式。 如果你想在执行这个循环之前完成所有这些,你必须使用IBAction方法。 或者,就像我在这里所build议的那样,只需在目标视图控制器中执行所有这些asynchronous请求。 出于某种原因,立即转换到目标视图控制器(并在那里显示UIActivityIndicatorView )比延迟目标视图控制器的呈现(即使它们在function上非常相似)更令人满意的UX。

你需要把你的代码改成这样的:

 -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{ [self doSomethingToGetKey:account completion:^{ [segue.destinationViewController setKey:_key]; }]; } -(void)doSomethingToGetKey:(ACAccount *)account completion:(void (^)(void)completion{ [_apiObject postReverseOAuthTokenRequest:^(NSString *authenticationHeader) { APIObject *api = [APIObject APIOSWithAccount:account]; [api verifyCredentialsWithSuccessBlock:^(NSString *username) { [api postReverseAuthAccessTokenWithAuthenticationHeader:authenticationHeader successBlock:^(NSString *oAuthToken, NSString *oAuthTokenSecret, NSString *userID, NSString *screenName) { _key = oAuthToken; _secret = oAuthTokenSecret; _userId = userID; if (completion) completion(); } errorBlock:^(NSError *error) { NSLog(@"ERROR 1, %@", [error localizedDescription]); exit(1); }]; } errorBlock:^(NSError *error) { NSLog(@"ERROR 2: %@",error.description); exit(1); }]; } errorBlock:^(NSError *error) { NSLog(@"ERROR 3: %@",error.description); exit(1); }]; };