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

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

];

;

【讨论】:

你必须从 successBlockpostReverseAuthAccessTokenWithAuthenticationHeader 方法调用完成块

以上是关于强制一种方法等待另一种方法完成的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法强制 PHP 等待 MySQL 完成事务?

selenium等待方式之显示等待

等待 Completable 未来线程完成的推荐方法是啥

显性等待的另一种写法

等待时间的三种方法

Python爬虫防封杀方法集合