在我切换到目标视图控制器之前的原始视图控制器中,我调用了一个获取我的关键参数的方法,然后在以下方法中在目标视图控制器中设置关键参数。但是,在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);
}];
};
答案 0 :(得分:3)
通常,模式涉及使用完成块,例如:
- (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,从而破坏了这个完成块模式的目的。
因此,您还希望更改按钮(或其他)以不执行segue本身,而是调用IBAction
,然后让 以编程方式执行segue({{ 3}})。因此,您最终得到的是IBAction
:
- (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调用,并删除活动指示器视图。这是对代码进行更为戏剧性的重构,但可能是一个更好的用户体验。
但是,底线,从prepareForSegue
内部你不能做异步,并期望目标视图控制器等待它,即使你使用完成块模式。如果要在执行segue之前完成所有这些操作,则必须使用IBAction
方法。或者,就像我在这里建议的那样,只需在目标视图控制器中执行所有这些异步请求。出于某种原因,立即转换到目标视图控制器(并在那里显示UIActivityIndicatorView
)比延迟目标视图控制器的显示更令人满意的用户体验(即使它们在功能上非常相似)。
答案 1 :(得分:2)
您需要将代码更改为:
-(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);
}];
};