2

I use the following method to attempt to synchronously obtain an OAuth access token within 10 seconds, otherwise return nil. It works fine, however as an exercise I would like to convert my code to use a semaphore.

The Runloop version

- (NSString*)oAuthAccessToken
{
    @synchronized (self)
    {
        NSString* token = nil;
        _authenticationError = nil;
        if (_authentication.accessToken)
        {
            token = [NSString stringWithFormat:@"Bearer %@", _authentication.accessToken];
        }
        else
        {
            [GTMOAuth2ViewControllerTouch authorizeFromKeychainForName:_keychainName authentication:_authentication];
            [_authentication authorizeRequest:nil delegate:self didFinishSelector:@selector(authentication:request:finishedWithError:)];
            for (int i = 0; i < 5; i++)
            {
                [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
                if (_authentication.accessToken)
                {
                    token = [NSString stringWithFormat:@"Bearer %@", _authentication.accessToken];
                    break;
                }
                else if (_authenticationError)
                {
                    break;
                }
            }
        }
//        LogDebug(@"Returning token: %@", token);
        return token;
    }
}

Semaphore Version

The semaphore version of the code goes a little something like this:

- (NSString*)oAuthAccessToken
{
    @synchronized (self)
    {
        NSString* token = nil;
        _authenticationError = nil;
        if (_authentication.accessToken)
        {
            token = [NSString stringWithFormat:@"Bearer %@", _authentication.accessToken];
        }
        else
        {
            _authorizationSemaphore = dispatch_semaphore_create(0);
            dispatch_async(_authorizationRequestQueue, ^(void)
            {
                [GTMOAuth2ViewControllerTouch authorizeFromKeychainForName:_keychainName authentication:_authentication];
                [_authentication authorizeRequest:nil delegate:self didFinishSelector:@selector(authentication:request:finishedWithError:)];
            });
            dispatch_semaphore_wait(_authorizationSemaphore, DISPATCH_TIME_FOREVER);
            if (_authentication.accessToken)
            {
                token = [NSString stringWithFormat:@"Bearer %@", _authentication.accessToken];
            }
        }
        return token;
    }
}

Gotcha!!! GTMOAuth2 sometimes returns immediately

  • When GTMOAuth2 needs to hit the network it calls back via a delegate method. In this method I signal my semaphore.
  • Sometimes GTMOAuth2 is able to return immediately. The problem is the method returns void.

How can I signal my semaphore in the latter case? If I add an observer to the authentication.assessToken will it be fired?

4

1 回答 1

3

我不熟悉 GTMOAuth2 库,但authentication.accessToken它是一个属性,因此似乎没有任何东西可以阻止它与 KVO 兼容。添加观察者应该在所有情况下都适合您,无论是异步还是同步。因此,我只考虑异步情况。

如果你想让你的解决方案更干净,那么你绝对应该尝试Reactive Cocoa

于 2013-09-26T09:49:32.683 回答