我知道有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首先触发。我希望它能排第二。我想念什么?

最佳答案

有两个想法:

  • 问题在于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默认将完成处理程序分派(dispatch)到主队列,并且您可能会死锁。
  • 顺便说一句,您永远不应阻塞主队列(UX较差,您的应用可能会被看门狗进程杀死,等等),因此,如果您从主队列中执行此操作,则我不建议您使用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);
    


  • 听起来您想让模型对象完成更新后,让模型让UI进行任何必要的更新。因此,您可以使用自己的块参数,以便 View Controller 可以告诉模型对象完成后的操作(而不是使用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);
                                                 }];
    }
    

    然后,您的 View Controller 可以执行以下操作:
    [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使用的样式相同的完成块。如果希望模型将信息传递回去,则可以将其他参数本身添加到完成模块中,但是我想上面的示例说明了基本思想。

    10-04 20:45