我在 Objective-C 中开发的服务遇到了一些难题。该服务的目的是解析核心数据实体列表并为每个对象下载相应的图像文件。该服务的原始设计因太多同时下载请求而阻塞了我的网络服务器。为了解决这个问题,我将负责执行下载请求的代码移至递归方法中。每个下载请求的完成处理程序都会再次调用该方法,从而确保每次下载都将等待前一个下载完成后再分派(dispatch)。
事情变得棘手的是负责实际更新我的核心数据模型和进度指示器 View 的代码。在下载的完成处理程序中,在方法递归之前,我对负责更新核心数据的 block 进行异步调用,然后更新 View 以显示进度。该 block 需要有一个变量来跟踪该 block 已执行的次数。在原始代码中,我可以简单地拥有一个具有 block 作用域的方法级变量,该变量将在 block 内递增。由于该方法现在是递归的,因此该策略不再有效。方法级别变量只会在每次递归时重置。由于 block 调用的异步性质,我不能简单地将变量传递到下一个级别。
我在这里完全不知所措。谁能建议一种处理这个问题的方法?
更新: 正如 Matt 下面指出的,这里的核心问题是如何控制请求的时间。经过更多研究后,我发现了为什么我的原始代码不起作用。事实证明,一旦第一个任务启动,超时间隔就开始运行,一旦时间到了,任何其他请求都将失败。如果您确切知道所有请求将花费多少时间,则可以简单地增加请求的超时时间。然而,更好的方法是使用 NSOperationQueue 来控制何时分派(dispatch)请求。有关如何执行此操作的一个很好的示例,请参阅:https://code-examples.net/en/q/19c5248 如果您采用这种方法,请记住,您必须调用在 downloadTask 的完成处理程序上创建的每个操作的completeOperation() 方法。
一些示例代码:
-(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];
}
最佳答案
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
。
关于objective-c - 在 Objective-C 中从异步 block 增加变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52320986/