【问题标题】:Best practice for making async calls sync in iOS??在 iOS 中进行异步调用同步的最佳实践?
【发布时间】:2013-03-08 01:36:00
【问题描述】:

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

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

目前,我们的 ViewControllers 的 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];
}

【问题讨论】:

  • 如果用户默认值中不存在else 部分,您需要适当地处理这些值。在主屏幕中显示加载覆盖或类似内容,让用户等待。在后台执行asyncObtainCredentials,一旦您收到回复,请继续下一步。为此,我建议您在应用启动期间检查这些值是否在用户默认值中,并调用以获取它。
  • @ACB 是的,这就是我们过去的做法。这意味着我不认为我们可以在这个客户端的范围内做到这一点,但必须有更大的东西涉及到调用它的视图控制器
  • 是的,如果您的视图控制器依赖于此 APIClient,您需要有一些选项来通知视图控制器您已获取它。

标签: iphone ios objective-c concurrency afnetworking


【解决方案1】:

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

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

【讨论】:

  • 问题在于,如果我们的服务器在应用程序启动期间关闭,那么当服务器重新启动时我们将收到 401 错误
  • @iaman00b, 如果你的服务器宕机了,用户还能使用这个应用吗?如果是,那么您不必显示加载覆盖。选择第二种方法。只是通知什么的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-29
  • 1970-01-01
  • 1970-01-01
  • 2015-09-02
  • 2010-10-09
  • 1970-01-01
相关资源
最近更新 更多