You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用NSURLSession后台上传文件时didCompleteWithError回调task参数为nil求助

解决iOS 11后台NSURLSessionUploadTask完成回调中task为nil的问题

我来帮你捋捋这个iOS 11后台上传遇到的task为nil的问题——我之前也碰到过类似的情况,尤其是在iOS 11这个版本里,后台NSURLSession的一些行为确实有点“反直觉”。结合你的描述,给你几个针对性的解决思路:

核心原因:后台Session的任务生命周期由系统托管

后台NSURLSession的任务是交给系统来管理的,哪怕你的App处于后台甚至被临时唤醒,系统都可能会回收原来的task实例,导致URLSession:task:didCompleteWithError:回调里拿到的task参数为nil。但之前的URLSession:dataTask:didReceiveData:回调触发时,任务还在活跃状态,所以task实例还存在。

具体解决办法

1. 不要依赖回调传入的task,通过Session主动获取任务

在完成回调里,放弃直接使用传入的task,转而调用session的getTasksWithCompletionHandler:方法获取当前所有任务,再通过提前保存的taskIdentifier匹配到对应的上传任务:

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
    // 不要直接用传入的task,大概率为nil
    [session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
        // 遍历上传任务列表,匹配你之前保存的taskIdentifier
        for (NSURLSessionUploadTask *targetTask in uploadTasks) {
            if (targetTask.taskIdentifier == self.savedUploadTaskIdentifier) {
                // 在这里处理任务完成的逻辑,比如检查error、更新上传状态等
                NSLog(@"任务完成,ID:%ld", (long)targetTask.taskIdentifier);
                break;
            }
        }
    }];
}

⚠️ 注意:创建上传任务时,一定要把task.taskIdentifier保存到本地(比如属性、UserDefaults),这样后续才能准确匹配到对应的任务。

2. 检查后台Session的配置与AppDelegate实现

iOS 11对后台Session的配置要求很严格,稍有不慎就会出现异常:

  • 确保后台Session是用backgroundSessionConfigurationWithIdentifier:创建的,且标识符唯一,不要重复创建相同ID的Session(否则系统会复用旧Session,引发状态混乱)。
  • 必须在AppDelegate中正确实现application:handleEventsForBackgroundURLSession:completionHandler:方法,这个方法是系统唤醒App处理后台任务完成事件的关键:
@property (nonatomic, copy) void (^backgroundSessionCompletionHandler)(void);

- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)(void))completionHandler {
    // 保存completionHandler,等所有任务处理完再调用
    self.backgroundSessionCompletionHandler = completionHandler;
    
    // 获取对应的后台Session
    NSURLSession *session = [self getBackgroundSessionWithIdentifier:identifier];
    if (session) {
        // 刷新任务状态,确认所有任务都处理完毕后再通知系统
        [session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
            if (dataTasks.count == 0 && uploadTasks.count == 0 && downloadTasks.count == 0) {
                if (self.backgroundSessionCompletionHandler) {
                    void (^handlerCopy)(void) = self.backgroundSessionCompletionHandler;
                    self.backgroundSessionCompletionHandler = nil;
                    handlerCopy();
                }
            }
        }];
    }
}

// 封装获取后台Session的方法
- (NSURLSession *)getBackgroundSessionWithIdentifier:(NSString *)identifier {
    static NSMutableDictionary *sessionDict;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sessionDict = [NSMutableDictionary dictionary];
    });
    
    NSURLSession *session = sessionDict[identifier];
    if (!session) {
        NSURLSessionConfiguration *config = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:identifier];
        session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
        sessionDict[identifier] = session;
    }
    return session;
}

3. 确保任务实例的引用被正确持有

虽然你提到didReceiveData:里task不为nil,但还是要检查代码中是否在任务完成前持有了task的强引用(比如把任务加到一个全局数组里)。如果没有持有,系统可能会提前释放task实例,导致后续回调中无法获取到有效实例。

总结

iOS 11的后台NSURLSession确实存在一些任务回调的小bug,但核心解决方案就是放弃依赖回调传入的task参数,通过Session主动获取任务并匹配,同时保证后台Session的配置和AppDelegate的相关方法正确实现,这样就能解决task为nil的问题了。

内容的提问来源于stack exchange,提问作者adienthu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 03:39:49