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

NSURLSessionDataTask偶发崩溃求助:dataTaskWithRequest调用异常

问题描述

我开发了一款支持异步刷新的天气APP,自己测试从未遇到崩溃,但苹果崩溃报告显示每天约有一次崩溃,且不局限于特定设备。APP用户量不小,每几分钟就会刷新一次,但我不清楚用户的崩溃报告上报比例,没法判断崩溃实际有多罕见。我已经试过一些方案,比如确保创建dataTask的Async Downloader类不被销毁等。目前有两种上报的崩溃类型,最常见的发生在以下代码处:

-(void)startDownload
{
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:fileURL] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:12];
    NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];

    if (!session || !request || ![session respondsToSelector:@selector(dataTaskWithRequest:completionHandler:)])
        return;
    // 堆栈追踪指向下方代码崩溃
    self.dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {...}
    // ...
}

上述“防御性”if语句是合理性检查,崩溃堆栈追踪如下:
崩溃堆栈

其中self.dataTask的定义为@property NSURLSessionDataTask *dataTask;。

补充说明:我无法通过Xcode查看崩溃转储的更多信息,因此用文本编辑器打开获取了完整信息:

Exception Type:  EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Triggered by Thread:  9

Last Exception Backtrace:
0   CoreFoundation                  0x1ca108cb4 __exceptionPreprocess + 164 (NSException.m:202)
1   libobjc.A.dylib                 0x1c32243d0 objc_exception_throw + 60 (objc-exception.mm:356)
2   CoreFoundation                  0x1ca27dab8 -[NSObject(NSObject) doesNotRecognizeSelector:] + 136 (NSObject.m:140)
3   CoreFoundation                  0x1ca11f0e8 ___forwarding___ + 1592 (NSForwarding.m:3578)
4   CoreFoundation                  0x1ca185900 _CF_forwarding_prep_0 + 96 (:-1)
5   App                             0x1009667cc -[AsyncDownloader startDownload] + 396 (AsyncDownloader.m:79)

触发崩溃的线程信息:

Thread 9 name:
Thread 9 Crashed:
0   libsystem_kernel.dylib          0x0000000208743558 __pthread_kill + 8 (:-1)
1   libsystem_pthread.dylib         0x0000000229411118 pthread_kill + 268 (pthread.c:1670)
2   libsystem_c.dylib               0x00000001d162b178 abort + 180 (abort.c:118)
3   libc++abi.dylib                 0x000000022934fbf8 abort_message + 132 (:-1)
4   libc++abi.dylib                 0x000000022933f444 demangling_terminate_handler() + 348 (:-1)
5   libobjc.A.dylib                 0x00000001c3229ea4 _objc_terminate() + 144 (objc-exception.mm:498)
6   libc++abi.dylib                 0x000000022934efbc std::__terminate(void (*)()) + 16 (:-1)
7   libc++abi.dylib                 0x000000022934ef60 std::terminate() + 56 (:-1)
8   libdispatch.dylib               0x00000001d15caec0 _dispatch_client_callout + 40 (object.m:563)
9   libdispatch.dylib               0x00000001d15ce330 _dispatch_continuation_pop + 504 (queue.c:306)
10  libdispatch.dylib               0x00000001d15e1908 _dispatch_source_invoke + 1588 (source.c:961)
11  libdispatch.dylib               0x00000001d15cde6c _dispatch_queue_override_invoke + 500 (queue.c:0)
12  libdispatch.dylib               0x00000001d15dc944 _dispatch_root_queue_drain + 396 (queue.c:7051)
13  libdispatch.dylib               0x00000001d15dd158 _dispatch_worker_thread2 + 164 (queue.c:7119)
14  libsystem_pthread.dylib         0x000000022940ada0 _pthread_wqthread + 228 (pthread.c:2631)
15  libsystem_pthread.dylib         0x000000022940ab7c start_wqthread + 8 (:-1)


Thread 9 crashed with ARM Thread State (64-bit):
    x0: 0x0000000000000000   x1: 0x0000000000000000   x2: 0x0000000000000000   x3: 0x0000000000000000
    x4: 0x0000000229353647   x5: 0x000000016fa2acb0   x6: 0x000000000000006e   x7: 0x0000000000002700
    x8: 0xc5115eb4a9cc1e34   x9: 0xc5115eb5c66eae34  x10: 0x0000000000000200  x11: 0x000000000000000b
   x12: 0x000000000000000b  x13: 0x00000000001ff800  x14: 0x00000000000007fb  x15: 0x000000009002802e
   x16: 0x0000000000000148  x17: 0x000000016fa2b000  x18: 0x0000000000000000  x19: 0x0000000000000006
   x20: 0x000000000001e10f  x21: 0x000000016fa2b0e0  x22: 0x0000000000000110  x23: 0x0000000000000000
   x24: 0x0000000000000000  x25: 0x000000016fa2b0e0  x26: 0x0000000000030008  x27: 0x0000000282e3d180
   x28: 0x0000000220904c80   fp: 0x000000016fa2ac20   lr: 0x0000000229411118
    sp: 0x000000016fa2ac00   pc: 0x0000000208743558 cpsr: 0x40001000
   esr: 0x56000080  Address size fault

请问有哪些排查方向或尝试方案可以避免该崩溃?整体来看崩溃相当罕见,我怀疑是APP被系统终止导致的异常退出,但仍欢迎任何建议。


排查与解决方案

从崩溃堆栈来看,核心问题是-[NSObject(NSObject) doesNotRecognizeSelector:],即调用了某个对象不存在的方法。结合代码和堆栈信息,给出以下排查方向和修复方案:

1. 确认self的有效性

崩溃发生在给self.dataTask赋值时,有可能此时self已经被释放,变成野指针(僵尸对象)。即便你已经确保AsyncDownloader不被销毁,异步场景下仍可能出现对象生命周期异常:

  • 给AsyncDownloader的dataTask属性添加nonatomic, strong修饰(未添加的话),保证赋值时内存管理正确。
  • 在startDownload方法开头增加self有效性检查:
    if (!self) return;
    
  • 闭包中使用弱引用+强引用捕获,避免循环引用或野指针:
    __weak typeof(self) weakSelf = self;
    self.dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        __strong typeof(weakSelf) strongSelf = weakSelf;
        if (!strongSelf) return;
        // 后续处理逻辑
    }];
    

2. 优化NSURLSession的生命周期

每次调用startDownload都创建新的NSURLSession,不仅可能导致资源泄漏,还可能在极端场景下引发对象状态异常:

  • 改为全局复用一个NSURLSession实例,在AsyncDownloader的初始化方法中创建一次,后续复用。
  • 校验NSURLSession的配置参数,避免因配置异常导致创建出的session对象状态异常。

3. 完善防御性检查逻辑

当前的if检查了session、request和session的方法响应,可进一步细化:

  • 提前检查fileURL是否为空或无效,避免创建无效NSURL:
    if (!fileURL || fileURL.length == 0) return;
    NSURL *url = [NSURL URLWithString:fileURL];
    if (!url) return;
    NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:12];
    
  • 赋值self.dataTask前,取消之前的运行中任务,避免任务叠加导致异常:
    if (self.dataTask && self.dataTask.state == NSURLSessionTaskStateRunning) {
        [self.dataTask cancel];
    }
    

4. 验证系统终止导致的异常

你怀疑是系统终止引发崩溃,可从以下方面验证:

  • 这类崩溃的Exception Codes为0x0000000000000000,大概率是系统因内存不足或后台超时终止APP。可在APP中加入内存监控,记录内存使用情况,同时优化后台刷新逻辑:
    • 限制后台刷新频率,避免短时间内发起过多请求。
    • 后台任务完成后及时清理资源,释放内存。
  • 使用苹果MetricKit框架收集性能数据,包括内存使用、后台任务执行时长等,辅助判断是否为系统终止导致的崩溃。

5. 增加日志与监控

为了复现或定位问题,需补充关键节点的日志:

  • 在startDownload方法的开头、关键检查点、赋值dataTask前后添加日志,记录self、session、request的内存地址和状态。
  • 接入第三方崩溃监控工具(如Firebase Crashlytics),获取更详细的崩溃上下文,包括设备系统版本、APP版本、崩溃前操作路径等。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 16:17:34