等待异步调用在完成块内完成

我目前正在开发一个POC应用程序, 我之前在此发布过它 。 我试图处理身份validation令牌的自动刷新,如果我的服务器给我401错误(未经授权)。

这是我的演示函数,它从服务器请求一些信息(我可以故意发送它有效/无效的auth令牌)

NSInteger retryAttempts = 0; NSInteger retryMax = 1; - (void) requestDataForUser { NSLog(@"requestDataForUser - Called"); //Indicate Network Activity dispatch_async(dispatch_get_main_queue(), ^{ [UIApplication sharedApplication].networkActivityIndicatorVisible = TRUE; }); //Build request URL String NSString *requestString = [NSString stringWithFormat:@"%@%@%@",baseURL,requestURL,@"3"];//Change to allow change in username here. //Get auth token NSString *accessToken = [SAMKeychain passwordForService:kServer account:kKeyAccessToken]; NSString *requestAuthorization = [NSString stringWithFormat:@"%@ %@", @"Bearer", accessToken]; //Initialize url request NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init]; //Set the url for the request [request setURL:[NSURL URLWithString:requestString]]; //Set HTTP method for request [request setHTTPMethod:@"GET"]; //Set HTTP header field with the authorization token [request setValue:requestAuthorization forHTTPHeaderField:@"Authorization"]; //Create full request NSURLSession *session = [NSURLSession sharedSession]; __weak typeof (self) weakSelf = self; NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){ NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response; NSLog(@"Status Code: %ld\n",(long)httpResponse.statusCode); NSString *message = [NSHTTPURLResponse localizedStringForStatusCode:httpResponse.statusCode]; NSLog(@"Message: %@", message); NSLog(@"requestDataForUser - Responce from server"); //Check for an error, if there is no error we proceed. if (!error) { if (retryAttempts <= retryMax) { switch (httpResponse.statusCode) { case 200 ... 299: NSLog(@"SUCCESS"); NSLog(@"Performing any completion related functions!"); break; case 401: NSLog(@"401 Challenge - Retrying Authentication, Attempt %ld", (long)retryAttempts); [weakSelf refreshAuth]; [weakSelf requestDataForUser];//retries this function retryAttempts += 1; break; }} else { NSLog(@"401 Error Recieved - Retried credentials %ld time(s), please check your details are correct", (long)retryMax); retryAttempts = 0; //Reset retry counter //Alert controller? } //Get que and perform any UI changes dispatch_async(dispatch_get_main_queue(), ^{ [UIApplication sharedApplication].networkActivityIndicatorVisible = FALSE; }); } else { //Failed request NSLog(@"requestDataForUser - error : %@", error.description); dispatch_async(dispatch_get_main_queue(), ^{ [UIApplication sharedApplication].networkActivityIndicatorVisible = FALSE; }); } }]; [dataTask resume]; } 

我遇到的问题出现在请求的401挑战部分。 我想要做的是请求/刷新一个新令牌(在最后一次迭代中刷新,但目前我的服务器在令牌刷新时有点击中/未命中,因此我在此示例中请求新令牌)。 那么让我们来看看我的服务器挑战部分:

 case 401: NSLog(@"401 Challenge - Retrying Authentication, Attempt %ld", (long)retryAttempts); [weakSelf refreshAuth]; [weakSelf requestDataForUser];//retries this function retryAttempts += 1; break; 

所以我在这里打印出尝试号码,我可以手动设置重试“块”的次数,直到它放弃并向用户抛出错误。 接下来,它将调用auth令牌,重试请求并将retryAttempts增加1。

我的问题是,当我请求一个新令牌时,我正在异步执行,因此请求被发送,然后我的函数重试自己(显然没有新的令牌),然后它抛出错误。 然后我的令牌返回并打印到控制台,新令牌成功返回。

我看过信号量,但我似乎无法让它们工作(因为我的requestAuthToken方法没有完成块)。 无论如何我可以强制auth请求同步吗?

我还试图让我的requestAuth方法返回一个BOOL并在401块中循环bool直到它变为true,但是它永远不会被设置为true而while循环会永远继续。

任何和所有帮助表示赞赏!

假设您可以更改requestAuth的实现,请向requestAuth函数添加完成处理程序参数。
在requestAuth实现中,在收到令牌后调用该处理程序。 然后在requestDataForUser中:

 case 401: NSLog(@"401 Challenge - Retrying Authentication, Attempt %ld", (long)retryAttempts); [weakSelf refreshAuth withCompletionHandler:weakself.requestDataForUser]; retryAttempts += 1; break; 

否则,使用NSOperationQueue并将最大并发操作设置为1:

 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; queue.maxConcurrentOperationCount = 1; [queue addOperationWithBlock:^{ [weakself requestAuth]; }]; [queue addOperationWithBlock:^{ [weakself requestDataForUser]; }];