我有一个单例类 APIClient,它需要设置 userId 和 authToken 才能调用我的后端。
我们目前将 userId 和 authToken 存储在 NSUserDefaults 中。对于全新安装,这些值不存在,我们会向服务器查询它们。
目前,我们在 ViewController 的 viewDidLoad 方法中有代码,如果这些值不存在,则可以手动查询服务器。
我有兴趣让这门课“正常工作”。我的意思是让客户端检查它是否已初始化,如果没有触发对服务器的调用并设置适当的 userId 和 authToken - 所有这些都无需手动干预。
这已被证明是一个相当棘手的问题,因为:
- 我无法使 asyncObtainCredentials 同步,因为#iphonedev 的人们告诉我,如果我必须冻结主线程以进行网络操作,操作系统将杀死我的应用程序
- 对于我们现在所拥有的,由于 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];
}