0

我有一个单例类 APIClient,它需要设置 userId 和 authToken 才能调用我的后端。

我们目前将 userId 和 authToken 存储在 NSUserDefaults 中。对于全新安装,这些值不存在,我们会向服务器查询它们。

目前,我们在 ViewController 的 viewDidLoad 方法中有代码,如果这些值不存在,则可以手动查询服务器。

我有兴趣让这门课“正常工作”。我的意思是让客户端检查它是否已初始化,如果没有触发对服务器的调用并设置适当的 userId 和 authToken - 所有这些都无需手动干预。

这已被证明是一个相当棘手的问题,因为:

  1. 我无法使 asyncObtainCredentials 同步,因为#iphonedev 的人们告诉我,如果我必须冻结主线程以进行网络操作,操作系统将杀死我的应用程序
  2. 对于我们现在所拥有的,由于 asyncObtainCredential 的异步特性,第一次调用总是会失败。将返回 Nil,第一次调用将始终失败。

有谁知道解决这个问题的好方法?

`

@interface APIClient ()
@property (atomic) BOOL initialized;
@property (atomic) NSLock *lock;
@end

@implementation APIClient

#pragma mark - Methods

- (void)setUserId:(NSNumber *)userId andAuthToken:(NSString *)authToken;
{
    self.initialized = YES;
    [self clearAuthorizationHeader];
    [self setAuthorizationHeaderWithUsername:[userId stringValue] password:authToken];
}

#pragma mark - Singleton Methods

+ (APIClient *)sharedManager {
    static dispatch_once_t pred;
    static APIClient *_s = nil;

    dispatch_once(&pred, ^{
        _s = [[self alloc] initWithBaseURL:[NSURL URLWithString:SERVER_ADDR]];
        _s.lock =[NSLock new] ;
    });

    [_s.lock lock];
    if (!(_s.initialized)) {
        NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
        NSNumber *userId = @([prefs integerForKey:KEY_USER_ID]);
        NSString *authToken = [prefs stringForKey:KEY_AUTH_TOKEN];

        // If still doesn't exist, we need to fetch
        if (userId && authToken) {
            [_s setUserId:userId andAuthToken:authToken];
        } else {
            /*
             * We can't have obtainCredentials to be a sync operation the OS will kill the thread
             * Hence we will have to return nil right now.
             * This means that subsequent calls after asyncObtainCredentials has finished
             * will have the right credentials.
             */
            [_s asyncObtainCredentials:^(NSNumber *userId, NSString *authToken){
                [_s setUserId:userId andAuthToken:authToken];
            }];
            [_s.lock unlock];
            return nil;
        }
    }
    [_s.lock unlock];

    return _s;
}

- (void)asyncObtainCredentials:(void (^)(NSNumber *, NSString *))successBlock {

    AFHTTPClient *client = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:SERVER_ADDR]];
    NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys:[OpenUDID value], @"open_udid", nil];
    NSMutableURLRequest *request = [client requestWithMethod:@"GET" path:@"/get_user" parameters:params];

    AFJSONRequestOperation *operation = \
    [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
        ... 

        // Do not use sharedManager here cause you can end up in a deadlock
        successBlock(userId, authToken);

    } failure:^(NSURLRequest *request , NSURLResponse *response , NSError *error , id JSON) {
        NSLog(@"obtain Credential failed. error:%@ response:%@ JSON:%@",
              [error localizedDescription], response, JSON);
    }];

    [operation start];
    [operation waitUntilFinished];
}
4

1 回答 1

2

NSUserDefaults您应该在应用程序启动期间检查这些值是否存在。如果它们不存在,请调用以从服务器获取它并在屏幕上显示加载覆盖。获取后,您可以继续下一步。

如果您不想使用加载覆盖,您可以isLoading在类中设置一些标志APIClient并检查以了解异步是否仍在获取。因此,每当您进行服务调用并且需要这些值时,您就知道如何根据此标志来处理它。获得所需的值并存储在 中后NSUserDefaults,您可以继续下一步。您可以使用 Notifications/Blocks/KVO 通知您的视图控制器,让他们知道您已经获取了这些值。

于 2013-03-08T01:54:46.703 回答