我正在学习 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 不关心他们正在下载什么(文件或任意数据或其他)。您只需要使用下载任务或上传任务,而不是数据任务。但你正在这样做。
在没有看到其余代码的情况下,我的猜测是 CustomURLRequest
是 NSURLRequest
的子类。不幸的是,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/