从Objective-C中的异步块递增变量 [英] Incrementing a Variable from an Asynchronous Block in Objective-C

查看:67
本文介绍了从Objective-C中的异步块递增变量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在Objective-C中正在使用的服务遇到了一个难题.该服务的目的是解析核心数据实体列表,并为每个对象下载相应的图像文件.该服务的原始设计使我的Web服务器无法同时进行太多下载请求.为了解决这个问题,我将负责执行下载请求的代码移到了递归方法中.每个下载请求的完成处理程序都会再次调用该方法,从而确保每次下载都将等待上一个下载完成后再进行分派.

I have run into a bit of a conundrum with a service I am working on in objective-c. The purpose of the service is to parse through a list of core-data entities and download a corresponding image file for each object. The original design of the service was choking my web-server with too many simultaneous download requests. To get around that, I moved the code responsible for executing the download request into a recursive method. The completion handler for each download request will call the method again, thus ensuring that each download will wait for the previous one to complete before dispatching.

棘手的地方是负责实际更新核心数据模型和进度指示器视图的代码.在下载的完成处理程序中,在方法递归之前,我进行了异步调用,该块负责更新核心数据,然后更新视图以显示进度.该块需要有一个变量来跟踪该块已执行了多少次.在原始代码中,我可以简单地使用一个方法级变量,其块范围将在块内递增.由于该方法现在是递归的,因此该策略不再起作用.方法级别变量将在每次递归时简单地重置.由于块调用的异步特性,我不能简单地将变量传递到下一个级别.

Where things get tricky is the code responsible for actually updating my core-data model and the progress indicator view. In the completion handler for the download, before the method recurses, I make an asynchronous call the a block that is responsible for updating the core data and then updating the view to show the progress. That block needs to have a variable to track how many times the block has been executed. In the original code, I could simply have a method-level variable with block scope that would get incremented inside the block. Since the method is recursive now, that strategy no longer works. The method level variable would simply get reset on each recursion. I can't simply pass the variable to the next level either thanks to the async nature of the block calls.

我在这里完全不知所措.谁能建议解决这个问题的方法?

I'm at a total loss here. Can anyone suggest an approach for dealing with this?

更新:正如马特在下面指出的那样,这里的核心问题是如何控制请求的时间安排.经过更多研究之后,我发现了为什么原始代码无法正常工作.事实证明,超时间隔在启动第一个任务后立即开始运行,并且一旦时间到了,任何其他请求都将失败.如果您确切知道所有请求将花费多少时间,则可以简单地增加请求的超时时间.但是,更好的方法是使用NSOperationQueue来控制何时分派请求.有关如何执行此操作的绝佳示例,请参见: https://code-examples.net/en/q/19c5248 如果采用这种方法,请记住,您将必须调用在downloadTask的完成处理程序上创建的每个操作的completeOperation()方法.

Update: As matt pointed out below, the core issue here is how to control the timing of the requests. After doing some more research, I found out why my original code was not working. As it turns out, the timeout interval starts running as soon as the first task is initiated, and once the time is up, any additional requests would fail. If you know exactly how much time all your requests will take, it is possible to simply increase the timeout on your requests. The better approach however is to use an NSOperationQueue to control when the requests are dispatched. For a great example of how to do this see: https://code-examples.net/en/q/19c5248 If you take this approach, keep in mind that you will have to call the completeOperation() method of each operation you create on the completion handler of the downloadTask.

一些示例代码:

-(void) downloadSkuImages:(NSArray *) imagesToDownload onComplete:(void (^)(BOOL update,NSError *error))onComplete
{
    [self runSerializedRequests:imagesToDownload progress:weakProgress downloaded:0 index:0 onComplete:onComplete ];
}

-(void)runSerializedRequests:(NSArray *) skuImages progress:(NSProgress *) progress downloaded:(int) totalDownloaded index:(NSUInteger) index onComplete:(void (^)(BOOL update,NSError *error))onComplete 
{
     int __block downloaded = totalDownloaded;

     TotalDownloadProgressBlock totalDownloadProgressBlock =  ^BOOL (SkuImageID *skuImageId, NSString  *imageFilePath, NSError *error) {
          if(error==nil) {
                  downloaded++;
                  weakProgress.completedUnitCount = downloaded;
                  //save change to core-data here
                  }
          else {
                        downloaded++;
                        weakProgress.completedUnitCount = downloaded;
                        [weakSelf setSyncOperationDetail:[NSString stringWithFormat:@"Problem downloading sku image %@",error.localizedDescription]];
                      }

          if(weakProgress.totalUnitCount==weakProgress.completedUnitCount) {
                              [weakSelf setSyncOperationIndicator:SYNC_INDICATOR_WORKING];
                              [weakSelf setSyncOperationDetail:@"All product images up to date"];
                              [weakSelf setSyncOperationStatus:SYNC_STATUS_SUCCESS];
                              weakProgress.totalUnitCount = 1;
                              weakProgress.completedUnitCount = 1;
                              onComplete(false,nil);
                              return true;
                          }
          return false;
     };

    NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:nil destination:nil
    completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {

                NSLog(@"finished download %u of %lu", index +1, (unsigned long)skuImages.count);
                if(error != nil)
                {                    
                    NSLog(@"Download failed for URL: %@ with error: %@",skuImage.url, error.localizedDescription);
                }
                else
                {
                    NSLog(@"Download succeeded for URL: %@", skuImage.url);
                }
                dispatch_async(dispatch_get_main_queue(), ^(void){

                    totalDownloadProgressBlock(skuImageId, imageFilePath, error);

                });

                [self runSerializedRequests:manager skuImages:skuImages progress:progress downloaded:downloaded index:index+1 onComplete:onComplete ];
            }];

            NSLog(@"Starting download %u of %lu", index +1, (unsigned long)skuImages.count);
            [downloadTask resume];
}

推荐答案

该服务的原始设计使我的Web服务器出现了太多同时下载请求的情况.为了解决这个问题,我将负责执行下载请求的代码移到了递归方法中.

The original design of the service was choking my web-server with too many simultaneous download requests. To get around that, I moved the code responsible for executing the download request into a recursive method.

但这不是解决问题的正确方法.将单个持久性自定义NSURLSession与您自己的配置一起使用,并设置配置的 httpMaximumConnectionsPerHost .

But that was never the right way to solve the problem. Use a single persistent custom NSURLSession with your own configuration, and set the configuration's httpMaximumConnectionsPerHost.

这篇关于从Objective-C中的异步块递增变量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆