防止并发方法执行和阻塞

时间:2016-09-05 10:42:32

标签: objective-c multithreading objective-c-blocks

我正在编写一个类来集中远程调用到我的后端。 调用必须使用oauth进行身份验证,因此在每种方法中,我首先检查我的令牌是否为valide,如果没有,我请求刷新一个。

每种方法都是这样的:

-(void)getRemoteDataAndRun:(nullable void(^)(NSDictionary * __nullable json))success
           orFail:(nullable void(^)(AFHTTPRequestOperation * __nullable operation, NSError * __nullable error))failure {
    [self checkOAuthTokenAndRun:^{
        // my remote call
        ...
        success(json);
    } orFail:failure];
}

方法 checkOAuthTokenAndRun 检查令牌有效性并在需要时请求新的令牌。 我的问题是我必须确保不会同时调用checkOAuthTokenAndRun以防止在刷新(写入)时读取凭据。

我尝试使用NSLock但使用块(多线程),我收到以下错误:

*** -[NSLock lock]: deadlock (<NSLock: 0x7ffd9313d9b0> '(null)')

这是完整的checkOAuthTokenAndRun方法:

- (void)checkOAuthTokenAndRun:(void (^)())action orFail:(nullable void(^)(AFHTTPRequestOperation * __nullable operation, NSError * __nullable error))failure
{
    NSLog(@"lock oauth check/refresh");
    [oauthLock lock];

    if (credential == nil || [credential isExpired])
    {
        NSLog(@"Credentials are null or expired");

        if (credential == nil || credential.refreshToken == nil)
        {
            NSLog(@"Credentials are null or anonymous+expired");
            [self getAnonymousCredentialsAndRun:^{

                NSLog(@"unlock oauth");
                [oauthLock unlock];
                action();

            } orFail:^(AFHTTPRequestOperation * _Nullable operation, NSError * _Nullable error) {

                NSLog(@"unlock oauth");
                [oauthLock unlock];
                failure(operation, error);

            }];
        }
        else
        {
            NSLog(@"Credentials are expired");

            AFOAuth2Manager *OAuth2Manager = [[AFOAuth2Manager alloc] initWithBaseURL:[NSURL URLWithString:BaseURL] clientID:clientId secret:clientSecret];

            [OAuth2Manager authenticateUsingOAuthWithURLString:@"/oauth/v2/token" refreshToken:credential.refreshToken success:^(AFOAuthCredential *_credential) {
                credential = _credential;

                NSLog(@"Received refreshed token");

                [AFOAuthCredential storeCredential:credential
                                withIdentifier:OAuthProviderIdentifier];

                NSLog(@"unlock oauth");
                [oauthLock unlock];

                action();

            } failure:^(NSError *error) {

                NSLog(@"Failed refreshing oauth token (%@)", [error localizedDescription]);

                // remove refresh token
                NSLog(@"Unable to get oauth token, delete credentials (%@)", [error localizedDescription]);
                [AFOAuthCredential deleteCredentialWithIdentifier:OAuthProviderIdentifier];

                credential = nil; //[AFOAuthCredential retrieveCredentialWithIdentifier:OAuthProviderIdentifier];

                NSLog(@"unlock oauth");
                [oauthLock unlock];

                failure(nil, error);
            }];
        }
    }
    else { // run the action
        NSLog(@"Credentials are valid (%@)", (credential.refreshToken.length ? @"refresh token defined" : @"refresh token not defined"));

        NSLog(@"unlock oauth");
        [oauthLock unlock];
        action();
    }
}

如您所见,我正在使用嵌套块,我不知道这是否是我遇到死锁的原因。

再次,我只想阻止 checkOAuthTokenAndRun 方法的并发执行

感谢您的帮助

1 个答案:

答案 0 :(得分:0)

网络呼叫是并发的,它们必须是并发的。您的代码的问题是您在另一个本身并发的方法中调用并发方法。解决您的问题:

首先调用方法checkOAuthTokenAndRun,如果成功(来自块的响应),则调用方法getRemoteDataAndRun。

[self checkOAuthTokenAndRun:^{

    if(success){
       [self getRemoteDataAndRun:^{

          }]
   }
  } orFail:failure];

//上面的代码片段只是解决问题的一个示例。您需要重新设计块参数