I've noticed that implementing NSURLSessionDataDelegate
and starting a task will very occasionally throw an EXC_BAD_ACCESS. The actual calling method that gives the error seems to vary but always comes from CFNetwork
. For the most part, the calling method comes from NSURLSession delegate_dataTask:didReceiveData:completionHandler
. I've attached two crash logs with different callers below. I've also attached my implementation of NSURLSessionDataDelegate
.
Unfortunately I cannot reliably reproduce the error, so I don't have an example script to share. Creating and starting Downloader
objects will eventually create the error. It does seem to happen more often with larger files. Have I implemented something wrong here? Is there a good way to debug from this stacktrace?
I've tested on iOS10 and 10.1.1 with the same results.
Implementation:
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() } }
Crash log #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
Crash log #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
UPDATE: I can now semi-reliably reproduce this error by running the loop pasted below in the iOS Simulator. This does not happen on iOS 9.3. If you run the code below, within a minute you should receive the error. Since it's very likely to occur in the simulator, compared with a device, I would assume it's a concurrency issue that becomes more likely with more processing power/cores. To reproduce error, run this:
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 }
What does EXC_BAD_ACCESS mean? EXC_BAD_ACCESS is an exception raised as a result of accessing bad memory. We're constantly working with pointers to memory in Swift that link to a specific memory address. An application will crash whenever we try to access a pointer that is invalid or no longer exists.
A protocol that defines methods that URL session instances call on their delegates to handle session-level events, like session life cycle changes.
To debug an EXC_BAD_ACCESS, you can generally find out the where the dangling pointer is by enabling zombie objects. Choose edit scheme, then Diagnostics tab in the Run section, then click the 'Zombie Objects' option. Another cause for EXC_BAD_ACCESS can be infinite recursion, which can be found by adding some logging.
After speaking with Apple Technical Support we confirmed that it's a bug within the libBacktraceRecording.dylib
library, which is used for debugging within Xcode. I have filed a bug report and have been told that it won't crash on a users device since this is a debug error that occurs within a library not present on most users' devices.
Try running in the Zombies Instrument. My guess is that your Downloader
class instance is getting deallocated while the NSURLSession is operating, so when it goes to call your didReceiveData method, the memory formerly occupied by your object contains something else. (That's what a zombie is.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With