从 Objective-C 中的异步块递增变量
Incrementing a Variable from an Asynchronous Block in Objective-C
我 运行 对我在 objective-c 中从事的一项服务感到困惑。该服务的目的是解析核心数据实体列表并为每个对象下载相应的图像文件。该服务的最初设计是由于同时下载请求太多而阻塞了我的网络服务器。为了解决这个问题,我将负责执行下载请求的代码移到了递归方法中。每个下载请求的完成处理程序将再次调用该方法,从而确保每个下载都将等待前一个下载完成后再发送。
事情变得棘手的地方是负责实际更新我的核心数据模型和进度指示器视图的代码。在下载的完成处理程序中,在方法递归之前,我对负责更新核心数据的块进行异步调用,然后更新视图以显示进度。该块需要有一个变量来跟踪该块已执行了多少次。在原始代码中,我可以简单地拥有一个具有块作用域的方法级变量,该变量将在块内递增。由于该方法现在是递归的,该策略不再有效。方法级变量将在每次递归时简单地重置。由于块调用的异步性质,我不能简单地将变量传递到下一个级别。
我在这里完全不知所措。任何人都可以建议处理此问题的方法吗?
更新:
正如matt在下面指出的,这里的核心问题是如何控制请求的时间。在做了更多研究之后,我发现了为什么我的原始代码不起作用。事实证明,超时间隔会在第一个任务启动后立即开始 运行ning,一旦时间到了,任何其他请求都会失败。如果您确切知道所有请求将花费多少时间,则可以简单地增加请求的超时时间。然而,更好的方法是使用 NSOperationQueue 来控制何时调度请求。有关如何执行此操作的一个很好的示例,请参见: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 中从事的一项服务感到困惑。该服务的目的是解析核心数据实体列表并为每个对象下载相应的图像文件。该服务的最初设计是由于同时下载请求太多而阻塞了我的网络服务器。为了解决这个问题,我将负责执行下载请求的代码移到了递归方法中。每个下载请求的完成处理程序将再次调用该方法,从而确保每个下载都将等待前一个下载完成后再发送。
事情变得棘手的地方是负责实际更新我的核心数据模型和进度指示器视图的代码。在下载的完成处理程序中,在方法递归之前,我对负责更新核心数据的块进行异步调用,然后更新视图以显示进度。该块需要有一个变量来跟踪该块已执行了多少次。在原始代码中,我可以简单地拥有一个具有块作用域的方法级变量,该变量将在块内递增。由于该方法现在是递归的,该策略不再有效。方法级变量将在每次递归时简单地重置。由于块调用的异步性质,我不能简单地将变量传递到下一个级别。
我在这里完全不知所措。任何人都可以建议处理此问题的方法吗?
更新: 正如matt在下面指出的,这里的核心问题是如何控制请求的时间。在做了更多研究之后,我发现了为什么我的原始代码不起作用。事实证明,超时间隔会在第一个任务启动后立即开始 运行ning,一旦时间到了,任何其他请求都会失败。如果您确切知道所有请求将花费多少时间,则可以简单地增加请求的超时时间。然而,更好的方法是使用 NSOperationQueue 来控制何时调度请求。有关如何执行此操作的一个很好的示例,请参见: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
.