强制一种方法等待另一种方法完成
Posted
技术标签:
【中文标题】强制一种方法等待另一种方法完成【英文标题】:Force one method to wait for another to complete 【发布时间】:2014-09-22 05:30:16 【问题描述】:在我转到目标视图控制器之前,在我的原始视图控制器中,我调用了一个获取我的关键参数的方法,然后我在我的目标视图控制器中设置了关键参数,方法如下。但是,key 参数是在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);
];
;
【问题讨论】:
你的 doSomethingToGetKey 是异步的吗? 是的,它是异步的 您需要更改doSomethingToGetKey
以接受完成时调用的完成块。然后在该完成块内调用[segue.destinationViewController setKey:_key]
。
你能贴出这个方法的代码吗?
是的,刚刚发布了代码。
【参考方案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
内完成所有这些操作。这完全改变了问题,因为在调用此异步方法之前仍会执行 segue,从而违背了此完成块模式的目的。
因此,您还希望更改按钮(或其他)以不执行 segue 本身,而是调用 IBAction
,然后让 以编程方式执行 segue (as shown here) .因此,您最终会得到一个 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
)比延迟目标视图控制器的呈现更令人满意(即使它们在功能上非常相似)。
【讨论】:
【参考方案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);
];
;
【讨论】:
你必须从successBlock
的 postReverseAuthAccessTokenWithAuthenticationHeader
方法调用完成块以上是关于强制一种方法等待另一种方法完成的主要内容,如果未能解决你的问题,请参考以下文章