ios swift exc-bad-access nsurlsession

ios - NSURLSession causando EXC_BAD_ACCESS



swift exc-bad-access (2)

Me he dado cuenta de que la implementación de NSURLSessionDataDelegate y el inicio de una tarea lanzarán ocasionalmente un EXC_BAD_ACCESS. El método de llamada real que da el error parece variar pero siempre proviene de CFNetwork . En su mayor parte, el método de llamada proviene de NSURLSession delegate_dataTask:didReceiveData:completionHandler . He adjuntado dos registros de bloqueo con diferentes llamadas a continuación. También he adjuntado mi implementación de NSURLSessionDataDelegate .

Desafortunadamente, no puedo reproducir el error de manera confiable, por lo que no tengo un script de ejemplo para compartir. Crear e iniciar objetos de Downloader eventualmente creará el error. Parece que ocurre más a menudo con archivos más grandes. ¿He implementado algo mal aquí? ¿Hay una buena manera de depurar de este seguimiento de pila?

He probado en iOS10 y 10.1.1 con los mismos resultados.

Implementación:

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() } }

Registro de bloqueos # 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

Registro de bloqueos # 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

ACTUALIZACIÓN: Ahora puedo reproducir este error de forma semi-confiable ejecutando el bucle pegado a continuación en el simulador de iOS. Esto no sucede en iOS 9.3. Si ejecuta el código a continuación, en un minuto debería recibir el error. Dado que es muy probable que ocurra en el simulador, en comparación con un dispositivo, asumo que es un problema de concurrencia que se vuelve más probable con más capacidad de procesamiento / núcleos. Para reproducir el error, ejecuta esto:

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 }


Después de hablar con el Soporte técnico de Apple, confirmamos que se trata de un error dentro de la biblioteca libBacktraceRecording.dylib , que se utiliza para la depuración dentro de Xcode. He presentado un informe de error y me han dicho que no se bloqueará en un dispositivo de usuarios, ya que se trata de un error de depuración que ocurre dentro de una biblioteca que no está presente en la mayoría de los dispositivos de los usuarios.


Intenta correr en el instrumento Zombies. Mi conjetura es que su instancia de clase Downloader se está desasignando mientras la NSURLSession está operativa, por lo que cuando se utiliza para llamar a su método didReceiveData, la memoria que antes ocupaba su objeto contiene otra cosa. (Eso es lo que es un zombie).