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

时间:2014-09-22 05:30:17

标签: ios objective-c

在我切换到目标视图控制器之前的原始视图控制器中,我调用了一个获取我的关键参数的方法,然后在以下方法中在目标视图控制器中设置关键参数。但是,在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);

}];

};

2 个答案:

答案 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);

}];

};