ios - NSURLSession 后台 session 中的 NSURLSessionDownloadTask 出错

标签 ios objective-c nsurlsession

我正在学习 NSURLSession 以制作自定义网络类,并偶然发现了一个非常不寻常的错误。

我的目标很简单,我想在完成后将 URL 的响应写入文件中。所以我创建了一个下载任务并将其分配给一个defaultSessionConfiguration。在将委托(delegate)分配给配置和不将委托(delegate)分配给配置的两种情况下(在这种情况下完成处理程序起作用)都有效。

现在我转向了backgroundSessionConfigurationWithIdentifier。后台 session 不支持 block ,因此委托(delegate)调用是强制性的。

每次都以错误结束。 报错如下

Printing description of error:
Error Domain=NSURLErrorDomain Code=-1 "unknown error" UserInfo={NSErrorFailingURLKey=http://__________________, NSErrorFailingURLStringKey=http://__________________, NSLocalizedDescription=unknown error}

我想我一定是后台配置写错了,所以我通过创建一个下载图像的演示下载任务并将这个任务添加到这个后台 session 来测试它。 这次成功了

下面给出了有效的代码:

+ (CustomNetwork *)sharedNetworkObject
{
     if(!netWorkObj)
     {
          netWorkObj = [[CustomNetwork alloc] init];

          //[netWorkObj prepareDataSession];

          //[netWorkObj prepareBackgroundSession];
     }
     return netWorkObj;
}
//
+ (NSOperationQueue *)responseQueue
{
     if(!queue)
     {
          queue = [[NSOperationQueue alloc] init];
     }
     return queue;
}

- (void)prepareDataSession
{
     if(!dataSession)
     {
          NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
          configuration.HTTPMaximumConnectionsPerHost = 5; // This means 1 session can hold to 5 connections
          configuration.timeoutIntervalForRequest = CONNECTION_TIME_OUT;
          configuration.timeoutIntervalForResource = CONNECTION_TIME_OUT;
          dataSession = [NSURLSession sessionWithConfiguration:configuration
                                                      delegate:nil //[CustomNetwork sharedNetworkObject]
                                                 delegateQueue:nil];
     }
}

- (void)prepareBackgroundSession
{
     if(!backgroundSession)
     {
          NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"backgroundSession"];
          configuration.HTTPMaximumConnectionsPerHost = 5; // This means 1 session can hold to 5 connections
          configuration.timeoutIntervalForRequest = CONNECTION_TIME_OUT;
          configuration.timeoutIntervalForResource = CONNECTION_TIME_OUT;
          configuration.discretionary = NO;// For optimizing
          backgroundSession = [NSURLSession sessionWithConfiguration:configuration
                                                      delegate:[CustomNetwork sharedNetworkObject]
                                                 delegateQueue:nil];
     }
}

+ (void)demoBackGroundSessionWorks
{
     NSURL * url = [NSURL URLWithString:@"https://www.wallpapereast.com/static/images/wallpaper-photos-42.jpg"];//@"http://www.hdwallpapersinn.com/wp-content/uploads/2012/09/HD-Wallpaper-1920x1080.jpg"];
     //NSURLSessionConfiguration * backgroundConfig = [NSURLSessionConfiguration backgroundSessionConfiguration:@"backgroundtask1"];

     [[CustomNetwork sharedNetworkObject] prepareBackgroundSession];

     NSURLSessionDownloadTask * downloadTask =[backgroundSession downloadTaskWithURL:url];
     [downloadTask resume];

}

给出失败的代码:

    +(void)createDownloadConnectionWithUrl:(NSString *)aUrl
                                                    operationKey:(NSString *)operationKey
                                                       jsonParam:(NSString *)jsonString
                                                      HTTPMethod:(NSString *)method
                                                  startImmediate:(BOOL)startImmediate
                                                    downloadPath:(NSString *)downloadPath

    {
         if([[aUrl trimmedString] length] && [self isValidMethod:method] && [[downloadPath trimmedString] length])
         {
              CustomURLRequest *dataRequest = [CustomNetwork requestURLWithString:aUrl];
              dataRequest.downloadDestinationPath = downloadPath;

              [self prepareTheDataRequest:&dataRequest
                           WithParameters:&jsonString
                                ForMethod:&method
                          andOpertaionKey:operationKey];


              // Remove any file if present in the path
              if([[NSFileManager defaultManager] fileExistsAtPath:downloadPath])
              {
                   [[NSFileManager defaultManager] removeItemAtPath:downloadPath error:nil];
              }

              // PS : if you are using BackGroundSession, completion block wont work and will give to a NSGenericException. Reason: 'Completion handler blocks are not supported in background sessions. Use a delegate instead.'

              [[CustomNetwork sharedNetworkObject] prepareBackgroundSession];
              NSURLSessionDownloadTask *downloadTask =  [backgroundSession downloadTaskWithRequest:dataRequest];

if(startImmediate)
          {
               [downloadTask resume];
          }
}

prepare 方法向请求添加 header 。

如果我使用 dataSession 而不是 backgroundSession

,同样失败的代码将起作用

Q: 同一个请求在defaultConfiguration中有效,在backgroundSession中失败。我是不是遗漏了什么,或者与 Apple Docs 的“仅支持上传和下载任务(无数据任务)”有关。

最佳答案

后台 session 不关心他们正在下载什么(文件或任意数据或其他)。您只需要使用下载任务或上传任务,而不是数据任务。但你正在这样做。

在没有看到其余代码的情况下,我的猜测是 CustomURLRequestNSURLRequest 的子类。不幸的是,NSURLRequest 的子类NSURLSession 支持——尤其是那些实际直接添加属性的子类。在各种版本的 iOS 和 OS X 中,您会遇到的错误从严重到完全损坏不等。我什至见过这样的情况:您从一个请求中取回自定义属性值,同时从另一个完全不同的请求中取回 URL 和 header 。

您应该在 NSURLRequest 上创建一个自定义类别,而不是子类化。在该类别中,创建 getter 和 setter 方法。

  • 使用 [NSURLProtocol propertyForKey:inRequest:] 作为你的 getter。
  • 使用 [NSURLProtocol setProperty:forKey:inRequest:] 作为你的 setter。

重要:使用这些方法存储在 NSURLRequest 中的所有对象必须是属性列表可序列化的。如果您需要将自定义类的实例与 NSURLRequest 相关联,您应该将它们存储在单独的 NSDictionary 中,并使用任意字典键(例如调用 [[NSUUID UUID] UUIDString]),您可以安全地将其存储在 NSURLRequest 对象中,稍后用于从外部字典获取自定义类。

关于ios - NSURLSession 后台 session 中的 NSURLSessionDownloadTask 出错,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40459260/

相关文章:

javascript - 使用四元数的设备方向

ios - 使用 NSFetchedResultsController 和 Core Data 移动时 UITableView 单元格消失

ios - iPhone 被锁定时无法访问 Documents 目录?

ios - 使用 UIDocumentPickerViewController,是否可以像在 Slack 中一样在首次打开时显示默认服务(Dropbox、Google Drive 等)?

objective-c - NSURLSessionDownloadTask 委托(delegate)未触发

ios - NSURLSessionUploadTask 作为单独的方法,但将数据返回给调用

android - 可以在安装后以编程方式更改应用程序图标吗?

iOS UIScrollView 只能水平滚动

ios - (Swift) 在 for 循环中使用带有 NSURLSession 的信号量

ios - NSURLSession 错误域=NSPOSIXErrorDomain 代码=2 "No such file or directory"