【问题标题】: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]。
  • 你能贴出这个方法的代码吗?
  • 是的,刚刚发布了代码。

标签: ios objective-c


【解决方案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 方法调用完成块
    猜你喜欢
    • 1970-01-01
    • 2021-07-21
    • 2014-05-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-27
    相关资源
    最近更新 更多