问题描述
在我切换到目标视图控制器之前的原始视图控制器中,我调用了一个获取我的关键参数的方法,然后在以下方法中在目标视图控制器中设置关键参数。但是,键参数在 doSomethingToGetKey
方法完成之前设置,因此传递空值。我想知道是否有办法让第二种方法等待第一种方法完成。
In my original view controller before I segue to the Destination View Controller, I call a method that gets my key parameter and I then set the key parameter in my destination view controller in the following method. However, the key parameter is set before the doSomethingToGetKey
method is completed so a null value is passed. I was wondering if there was a way to make the second method wait for the first one to finish.
原点视图控制器:
-(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);
}];
};
推荐答案
通常,模式涉及使用完成块,例如:
Generally the pattern involves employing a completion block, e.g.:
- (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
中完成所有这些操作。这完全改变了问题,因为在调用这个异步方法之前仍会执行segue,从而破坏了这个完成块模式的目的。
But, in this case, you're trying to do all of this inside prepareForSegue
. That changes the problem entirely, because the segue will still be performed before this asynchronous method is called, defeating the purpose of this completion block pattern.
你因此也想改变您的按钮(或其他)不执行segue本身,而是调用 IBAction
,然后让 以编程方式执行segue()。因此,你最终得到一个 IBAction
,如:
You therefore also want to change your button (or whatever) to not perform the segue itself, but rather call an IBAction
, and then have that perform the segue programmatically (as shown here). Thus, you'd end up with an IBAction
like:
- (IBAction)tappedButton:(id)sender
{
// perhaps update UI so user knows something slow is happening first,
// e.g., 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调用,并删除活动指示器视图。这是对代码进行更为戏剧性的重构,但可能是更好的用户体验。
Alternatively, you can remove all of this, just perform the segue, but then have the destination view controller present itself, show the activity indicator view, perform the api calls, and remove the activity indicator view. This is a more dramatic refactoring of the code, but is probably a better UX.
但是,底线,从 prepareForSegue
中你不能做异步的事情,并期望目标视图控制器等待对于它,即使你使用完成块模式。如果要在执行segue之前完成所有这些操作,则必须使用 IBAction
方法。或者,就像我在这里建议的那样,只需在目标视图控制器中执行所有这些异步请求。出于某种原因,立即转换到目标视图控制器(并在那里显示 UIActivityIndicatorView
)比延迟目标视图控制器的显示更令人满意的用户体验(即使它们是功能非常相似)。
But, bottom line, from within prepareForSegue
you cannot do something asynchronous, and expect the destination view controller to wait for it, even if you employ the completion block pattern. You'd have to use the IBAction
approach if you want to do all of this before performing the segue. Or, like I suggested here, just do all of this asynchronous request stuff in the destination view controller. For some reason, transitioning immediately to the destination view controller (and showing the UIActivityIndicatorView
there) is more satisfying UX than delaying the presentation of the destination view controller (even though they're functionally very similar).
这篇关于强制一种方法等待另一种方法完成的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!