AFNetworking-2 waitUntilFinished无法正常工作

时间:2013-12-09 17:36:49

标签: ios objective-c afnetworking-2

我知道有another similar question,但它适用于较早版本的AFNetworking,并且无论如何都无法真正回答它。

我有以下代码:

AFHTTPRequestOperationManager* manager = [AFHTTPRequestOperationManager manager];
manager.securityPolicy.allowInvalidCertificates = YES;
manager.requestSerializer = [AFJSONRequestSerializer serializer];
[manager.requestSerializer setAuthorizationHeaderFieldWithUsername: currentUser() password: currentPassword()];
__block NSDictionary* response = nil;
AFHTTPRequestOperation* operation = [manager
    GET: @"https://10.20.30.40:8765/foobar"
    parameters: [NSDictionary dictionary]
    success:^(AFHTTPRequestOperation* operation, id responseObject){
        response = responseObject;
        NSLog(@"response (block): %@", response);
    }
    failure:^(AFHTTPRequestOperation* operation, NSError* error){
        NSLog(@"Error: %@", error);}
];
[operation waitUntilFinished];
NSLog(@"response: %@", response);
...

如果我运行这个,我在日志中会看到:

2013-12-09 09:26:20.105 myValve[409:60b] response: (null)
2013-12-09 09:26:20.202 myValve[409:60b] response (block): {
    F00005 = "";
    F00008 = "";
    F00013 = "";
}

NSLog之后waitUntilFinished首先触发。我预计它会第二次开火。我错过了什么?

1 个答案:

答案 0 :(得分:32)

有几点想法:

  1. 问题是waitUntilFinished将等待核心网络操作完成,但它不会等待successfailure完成块。如果要等待完成块,可以使用信号量:

    __block NSDictionary* response = nil;
    
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    
    manager.completionQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    AFHTTPRequestOperation* operation = [manager GET: @"https://10.20.30.40:8765/foobar"
                                          parameters: [NSDictionary dictionary]
                                             success:^(AFHTTPRequestOperation* operation, id responseObject){
                                                 response = responseObject;
                                                 NSLog(@"response (block): %@", response);
                                                 dispatch_semaphore_signal(semaphore);
                                             }
                                             failure:^(AFHTTPRequestOperation* operation, NSError* error){
                                                 NSLog(@"Error: %@", error);
                                                 dispatch_semaphore_signal(semaphore);
                                             }];
    
    NSLog(@"waiting");
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    // [operation waitUntilFinished];
    NSLog(@"response: %@", response);
    

    您可以将此包装在您自己的并发NSOperation子类中,在isFinished完成块中发布AFHTTPRequestOperation,从而消除此过程中的semaphore

    注意,如果在主队列上执行信号量,请务必指定completionQueue 因为,在没有这种情况下,AFNetworking默认将调度完成处理程序分配给主队列,您可以死锁。

  2. 顺便说一句,你永远不应该阻止主队列(可怜的用户体验,你的应用程序可能被监视程序进程杀死等等),所以如果你是从主队列中执行此操作,我会劝阻使用waitUntilFinished或信号量。最好只在完成块中启动你需要的任何东西,让主队列在这个异步网络操作正在进行时继续执行,例如:

    [activityIndicatorView startAnimating];
    
    AFHTTPRequestOperation* operation = [manager GET: @"https://10.20.30.40:8765/foobar"
                                          parameters: [NSDictionary dictionary]
                                             success:^(AFHTTPRequestOperation* operation, id responseObject){
    
                                                 // do whatever you want with `responseObject` here
    
                                                 // now update the UI, e.g.:
    
                                                 [activityIndicatorView stopAnimating];
                                                 [self.tableView reloadData];
                                             }
                                             failure:^(AFHTTPRequestOperation* operation, NSError* error){
    
                                                 // put your error handling here
    
                                                 // now update the UI, e.g.:
    
                                                 [activityIndicatorView stopAnimating];
                                             }];
    
    // NSLog(@"waiting");
    // dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    // // [operation waitUntilFinished];
    // NSLog(@"response: %@", response);
    

  3. 听起来你想让模型让UI在模型对象完成更新时进行任何必要的更新。因此,您可以使用自己的块参数,以便视图控制器可以告诉模型对象完成后要执行的操作(而不是使用waitUntilFinished或信号量来使网络操作阻塞主队列)。例如,让我们假设您的模型有这样的方法:

    - (void)updateModelWithSuccess:(void (^)(void))success failure:(void (^)(NSError *error))failure
    {
        AFHTTPRequestOperationManager* manager = [AFHTTPRequestOperationManager manager];
        manager.securityPolicy.allowInvalidCertificates = YES;
        manager.requestSerializer = [AFJSONRequestSerializer serializer];
        [manager.requestSerializer setAuthorizationHeaderFieldWithUsername: currentUser() password: currentPassword()];
        AFHTTPRequestOperation* operation = [manager GET: @"https://10.20.30.40:8765/foobar"
                                              parameters: [NSDictionary dictionary]
                                                 success:^(AFHTTPRequestOperation* operation, id responseObject){
    
                                                     // do your model update here
    
                                                     // then call the success block passed to this method (if any),
                                                     // for example to update the UI
    
                                                     if (success)
                                                         success();
                                                 }
                                                 failure:^(AFHTTPRequestOperation* operation, NSError* error){
                                                     NSLog(@"Error: %@", error);
    
                                                     // if caller provided a failure block, call that
    
                                                     if (failure)
                                                         failure(error);
                                                 }];
    }
    

    然后您的视图控制器可以执行以下操作:

    [modelObject updateModelWithSuccess:^{
        // specify UI updates to perform upon success, e.g.
        // stop activity indicator view, reload table, etc.
    } failure:^(NSError *error){
        // specify any UI updates to perform upon failure
    }]
    

    最重要的是,您的代码可以使用AFNetworking使用的相同样式的完成块。如果您希望模型返回信息,您可以将其他参数添加到完成块本身,但我认为上面说明了基本概念。