バックエンドを呼び出す前に、userId と authToken を設定する必要があるシングルトン クラス APIClient があります。
現在、userId と authToken を NSUserDefaults に保存しています。新規インストールの場合、これらの値は存在しないため、サーバーに照会します。
現在、ViewController の viewDidLoad メソッドには、これらの値が存在しない場合にサーバーに手動でクエリを実行するコードがあります。
このクラスを「うまく機能させる」ことに興味があります。これにより、クライアントが初期化されているかどうかをチェックし、そうでない場合はサーバーへの呼び出しを開始し、適切な userId と authToken を設定することを意味します-すべて手動の干渉なしで。
これは、次の理由により、かなりトリッキーであることが証明されています。
- #iphonedev の人々から、ネットワーク操作のためにメインスレッドをフリーズする必要がある場合、OS がアプリを強制終了すると言われたため、asyncObtainCredentials を同期化できません。
- 現時点では、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];
}