【问题标题】:NSURLSession causing EXC_BAD_ACCESSNSURLSession 导致 EXC_BAD_ACCESS
【发布时间】:2017-03-15 06:27:21
【问题描述】:

我注意到实现NSURLSessionDataDelegate 并开始一个任务偶尔会抛出一个EXC_BAD_ACCESS。给出错误的实际调用方法似乎有所不同,但总是来自CFNetwork。大多数情况下,调用方法来自NSURLSession delegate_dataTask:didReceiveData:completionHandler。我在下面附上了两个不同调用者的崩溃日志。我还附上了我对NSURLSessionDataDelegate 的实现。

很遗憾,我无法可靠地重现该错误,因此我没有可共享的示例脚本。创建和启动Downloader 对象最终会产生错误。对于较大的文件,它似乎更经常发生。我在这里实施了什么错误吗?有没有从这个堆栈跟踪中调试的好方法?

我在 iOS10 和 10.1.1 上进行了测试,结果相同。

实施:

class Downloader: NSObject, NSURLSessionDataDelegate {
    private let url: String
    var finished = false
    let finishCondition = NSCondition()

    init(url:String) {
        self.url = url
        super.init()
    }

    func start() {
        let config = NSURLSessionConfiguration.defaultSessionConfiguration()
        let session = NSURLSession(configuration: config,
                               delegate: self,
                               delegateQueue: nil)
        guard let u = NSURL(string: url) else {
            return
        }
        let request = NSMutableURLRequest(URL: u)
        let task = session.dataTaskWithRequest(request)
        task.resume()
    }

    func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask,
                    didReceiveData data: NSData) {
    }

    func URLSession(session: NSURLSession,
                    task: NSURLSessionTask,
                    didCompleteWithError error: NSError?) {
        session.invalidateAndCancel()
    }

    func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask,
                    didReceiveResponse response: NSURLResponse,
                                       completionHandler: (NSURLSessionResponseDisposition) -> Void) {
        completionHandler(NSURLSessionResponseDisposition.Allow)
    }

    func waitForFinish() {
        finishCondition.lock()
        while !finished {
            finishCondition.wait()
        }
        finishCondition.unlock()
    }

    func URLSession(session: NSURLSession, didBecomeInvalidWithError error: NSError?) {
        finishCondition.lock()
        finished = true
        finishCondition.broadcast()
        finishCondition.unlock()
    }
}

崩溃日志 #1:

* thread #5: tid = 0x25923, 0x0000000100042e8c libBacktraceRecording.dylib`__gcd_queue_item_enqueue_hook_block_invoke, queue = 'com.apple.NSURLSession-work', stop reason = EXC_BAD_ACCESS (code=1, address=0xf8686a68b98c6ec8)
  * frame #0: 0x0000000100042e8c libBacktraceRecording.dylib`__gcd_queue_item_enqueue_hook_block_invoke
    frame #1: 0x000000010004241c libBacktraceRecording.dylib`gcd_queue_item_enqueue_hook + 232
    frame #2: 0x000000010065dee8 libdispatch.dylib`_dispatch_introspection_queue_item_enqueue_hook + 40
    frame #3: 0x000000010063cba4 libdispatch.dylib`_dispatch_queue_push + 196
    frame #4: 0x000000018ba50500 Foundation`iop_promote_qos_outward + 112
    frame #5: 0x000000018ba4e524 Foundation`-[NSOperation setQualityOfService:] + 168
    frame #6: 0x000000018b9d7714 Foundation`-[NSOperationQueue addOperationWithBlock:] + 76
    frame #7: 0x000000018b73f82c CFNetwork`-[NSURLSession delegate_dataTask:didReceiveData:completionHandler:] + 208
    frame #8: 0x000000018b5a2c5c CFNetwork`-[__NSCFLocalSessionTask _task_onqueue_didReceiveDispatchData:completionHandler:] + 276
    frame #9: 0x000000018b5a5474 CFNetwork`-[__NSCFLocalSessionTask connection:didReceiveData:completion:] + 164
    frame #10: 0x000000018b647bf0 CFNetwork`__48-[__NSCFURLLocalSessionConnection _tick_running]_block_invoke + 120
    frame #11: 0x000000018b647b60 CFNetwork`-[__NSCFURLLocalSessionConnection _tick_running] + 344
    frame #12: 0x000000018b648c74 CFNetwork`-[__NSCFURLLocalSessionConnection _didReceiveData:] + 412
    frame #13: 0x000000018b64af8c CFNetwork`SessionConnectionLoadable::_loaderClientEvent_DidReceiveData(__CFArray const*) + 52
    frame #14: 0x000000018b6f823c CFNetwork`___ZN19URLConnectionLoader19protocolDidLoadDataEPK8__CFDatax_block_invoke_2 + 44
    frame #15: 0x000000018b64b58c CFNetwork`___ZN25SessionConnectionLoadable21withLoaderClientAsyncEU13block_pointerFvP21LoaderClientInterfaceE_block_invoke + 32
    frame #16: 0x000000010063125c libdispatch.dylib`_dispatch_call_block_and_release + 24
    frame #17: 0x000000010063121c libdispatch.dylib`_dispatch_client_callout + 16
    frame #18: 0x000000010063eb54 libdispatch.dylib`_dispatch_queue_serial_drain + 1136
    frame #19: 0x0000000100634ce4 libdispatch.dylib`_dispatch_queue_invoke + 672
    frame #20: 0x0000000100640e6c libdispatch.dylib`_dispatch_root_queue_drain + 584
    frame #21: 0x0000000100640bb8 libdispatch.dylib`_dispatch_worker_thread3 + 140
    frame #22: 0x000000018a01e2b8 libsystem_pthread.dylib`_pthread_wqthread + 1288
    frame #23: 0x000000018a01dda4 libsystem_pthread.dylib`start_wqthread + 4

崩溃日志 #2:

* thread #12: tid = 0x2521f, 0x000000010010ae8c libBacktraceRecording.dylib`__gcd_queue_item_enqueue_hook_block_invoke, queue = 'com.apple.CFNetwork.Connection', stop reason = EXC_BAD_ACCESS (code=1, address=0xd00f524835000200)
      * frame #0: 0x000000010010ae8c libBacktraceRecording.dylib`__gcd_queue_item_enqueue_hook_block_invoke
        frame #1: 0x000000010010a41c libBacktraceRecording.dylib`gcd_queue_item_enqueue_hook + 232
        frame #2: 0x0000000100759ee8 libdispatch.dylib`_dispatch_introspection_queue_item_enqueue_hook + 40
        frame #3: 0x0000000100738ba4 libdispatch.dylib`_dispatch_queue_push + 196
        frame #4: 0x00000001975ccb3c libnetwork.dylib`nw_connection_read + 448
        frame #5: 0x00000001975d938c libnetwork.dylib`tcp_connection_read + 168
        frame #6: 0x000000018b719d54 CFNetwork`TCPIOConnection::read(unsigned long, unsigned long, void (dispatch_data_s*, CFStreamError) block_pointer) + 172
        frame #7: 0x000000018b782af4 CFNetwork`HTTPEngine::_getBodyIntelligently(void (dispatch_data_s*, CFStreamError, bool) block_pointer) + 816
        frame #8: 0x000000018b780d0c CFNetwork`HTTPEngine::_readBodyStartNextRead() + 76
        frame #9: 0x000000018b783664 CFNetwork`___ZN10HTTPEngine21_getBodyIntelligentlyEU13block_pointerFvP15dispatch_data_s13CFStreamErrorbE_block_invoke.56 + 344
        frame #10: 0x000000018b719f64 CFNetwork`___ZN15TCPIOConnection4readEmmU13block_pointerFvP15dispatch_data_s13CFStreamErrorE_block_invoke + 480
        frame #11: 0x000000010072d25c libdispatch.dylib`_dispatch_call_block_and_release + 24
        frame #12: 0x000000010072d21c libdispatch.dylib`_dispatch_client_callout + 16
        frame #13: 0x000000010073ab54 libdispatch.dylib`_dispatch_queue_serial_drain + 1136
        frame #14: 0x0000000100730ce4 libdispatch.dylib`_dispatch_queue_invoke + 672
        frame #15: 0x000000010073ce6c libdispatch.dylib`_dispatch_root_queue_drain + 584
        frame #16: 0x000000010073cbb8 libdispatch.dylib`_dispatch_worker_thread3 + 140
        frame #17: 0x000000018a01e2b8 libsystem_pthread.dylib`_pthread_wqthread + 1288
        frame #18: 0x000000018a01dda4 libsystem_pthread.dylib`start_wqthread + 4

更新: 我现在可以通过在 iOS 模拟器中运行下面粘贴的循环来半可靠地重现此错误。这在 iOS 9.3 上不会发生。如果您运行下面的代码,您应该会在一分钟内收到错误消息。由于它很可能发生在模拟器中,与设备相比,我认为这是一个并发问题,随着更多的处理能力/内核变得更有可能。 要重现错误,请运行:

var i = 0
while true {
    print("running: \(i)")
    // random url, larger files seem more likely to cause error
    let url = "http://qthttp.apple.com.edgesuite.net/1010qwoeiuryfg/3340/33409.ts"
    let c = Downloader(url: url)
    c.start()
    c.waitForFinish()
    i += 1
}

【问题讨论】:

  • 在 iOS 10 中看到了这一点,但在 9.3 模拟器中也没有看到。相同的症状,相同的回溯,没有线索。
  • 我在 10.0 模拟器上看到了类似的崩溃 * thread #64: tid = 0x5e3688, 0x000000010717deac libBacktraceRecording.dylib`__gcd_queue_item_enqueue_hook_block_invoke + 4, queue = 'com.apple.network.connections', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
  • 很高兴我不是唯一一个。有什么好的方法可以调试吗?

标签: ios swift exc-bad-access nsurlsession


【解决方案1】:

尝试在 Zombies Instrument 中跑步。我的猜测是您的 Downloader 类实例在 NSURLSession 运行时被释放,因此当它调用您的 didReceiveData 方法时,您的对象以前占用的内存包含其他内容。 (这就是僵尸。)

【讨论】:

  • 我无法在 Zombie Instrument 中重现错误。但是,从“编辑方案”打开“僵尸对象”确实会导致相同的错误,而不会输出任何僵尸对象警告。这两者有区别吗?
  • 我看到了类似的崩溃,当它发生时我确实有僵尸,但它没有捕获任何东西。
  • 我可以确认,从 Xcode 9 开始,Sierra 10.12.5 上的 beta 5 仍然会发生这种情况。
【解决方案2】:

在与 Apple 技术支持人员交谈后,我们确认这是 libBacktraceRecording.dylib 库中的一个错误,该库用于在 Xcode 中进行调试。我提交了一份错误报告,并被告知它不会在用户设备上崩溃,因为这是在大多数用户设备上不存在的库中发生的调试错误。

【讨论】:

  • 您能否分享有关错误报告的链接或更多信息?那么其他人可以查看其进度吗?并且好奇您究竟是如何与 Apple 技术支持取得联系的?他们反应灵敏吗?
  • 我还没有把它放在 openradar 上,但苹果通常很少给出关于进展的反馈。您可以通过developer.apple.com/support/technical 联系 Apple 以获取开发者技术支持。他们通常会在下一个工作日之前回复,非常及时。
  • 我在 Sierra 的 Mac 上遇到了完全相同的崩溃;相同的症状,(EXC_BAD_ACCESS 在 NSURLConnectionLoader 代码深处,但仅来自 Xcode 调试会话);任何guard Malloc,地址消毒剂等都可以修复它。 Mac上用的是同一个dylib
  • @user1479585 您是否从 Apple 收到了有关此错误的任何更新?我似乎对 Xcode 8.2 也有同样的问题。
  • 没有更新。在与 Apple Dev 支持人员交谈时,他们说我不应该经常创建新的 NSURLSession 对象,它们应该是长期存在的对象。所以我实现了一个多路复用器,以便多个委托可以在同一个 NSURLSession 中运行,这大大减少了错误发生的次数
猜你喜欢
  • 1970-01-01
  • 2012-07-27
  • 2016-11-19
  • 2012-10-02
  • 2011-04-14
  • 2014-09-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多