【问题标题】:AFNetworking-2 waitUntilFinished not workingAFNetworking-2 waitUntilFinished 不起作用
【发布时间】:2013-12-09 17:36:48
【问题描述】:

我知道有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 之后首先触发的。我预计它会第二次开火。我错过了什么?

【问题讨论】:

    标签: ios objective-c afnetworking-2


    【解决方案1】:

    一些想法:

    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);
      

    听起来你想让你的模型在模型对象完成更新后让 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 使用的相同样式的完成块。如果您希望模型将信息传回,您可以向完成块本身添加其他参数,但我认为以上说明了基本思想。

    【讨论】:

    • 不幸的是,选项 #1 完全挂起。两个块都不会触发。不知道为什么把它放在那里,但确实如此。
    • 我理解“挂 UI 是不好的”的事情。在这种情况下,我遇到的问题是用于更新本地模型的 REST 查询。所以我想把这些 AF 的东西放在模型类中去进行更新。所以我最终不得不用 andWhenYourDone: 块来增加这些方法中的任何一个,这样我就可以在适当的时候触发 UI 更新。
    • @TravisGriggs 然后只需提供模型的updateModel 方法块参数(与AFNetworking 本身使用的successfailure 块不同)。这样,视图控制器可以有效地说“更新模型,完成后执行 x、y 和 z”。请参阅修改后的答案。
    • @TravisGriggs 请参阅 stackoverflow.com/questions/4326350/… - 第一个答案及其 cmets 了解更多详情
    • @TravisGriggs 我通过指定completionQueue 属性(几个月前我在pull request 中添加)修改了#1 以防止从主队列执行此操作时出现死锁。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多