导致 EXC_BAD_ACCESS 的 NSURLSession
NSURLSession causing EXC_BAD_ACCESS
我注意到执行 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 不会发生这种情况。如果您 运行 下面的代码,您应该会在一分钟内收到错误消息。由于它很可能发生在模拟器中,与设备相比,我认为这是一个并发问题,随着更多处理 power/cores 变得更有可能。
要重现错误,运行 这个:
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
}
在 Zombies Instrument 中尝试 运行。我的猜测是你的 Downloader
class 实例在 NSURLSession 运行时被释放,所以当它调用你的 didReceiveData 方法时,你的对象以前占用的内存包含其他东西。 (这就是僵尸。)
在与 Apple 技术支持交谈后,我们确认这是 libBacktraceRecording.dylib
库中的错误,用于在 Xcode 中进行调试。我已经提交了一份错误报告,并被告知它不会在用户设备上崩溃,因为这是一个调试错误,发生在大多数用户设备上不存在的库中。
我注意到执行 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 不会发生这种情况。如果您 运行 下面的代码,您应该会在一分钟内收到错误消息。由于它很可能发生在模拟器中,与设备相比,我认为这是一个并发问题,随着更多处理 power/cores 变得更有可能。 要重现错误,运行 这个:
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
}
在 Zombies Instrument 中尝试 运行。我的猜测是你的 Downloader
class 实例在 NSURLSession 运行时被释放,所以当它调用你的 didReceiveData 方法时,你的对象以前占用的内存包含其他东西。 (这就是僵尸。)
在与 Apple 技术支持交谈后,我们确认这是 libBacktraceRecording.dylib
库中的错误,用于在 Xcode 中进行调试。我已经提交了一份错误报告,并被告知它不会在用户设备上崩溃,因为这是一个调试错误,发生在大多数用户设备上不存在的库中。