问题
I'm sure this is something stupid that i'm doing, but after an hour I cannot see it. Maybe you can. Edit: this is on a device (iPhone 5S w/iOS 8.4), not the simulator.
I have an iOS 8 app with a DownloadManager, a singleton that subclasses AFHTTPSessionManager using a BackgroundSession. It used to work fine, but now I've done something and the result is that AFNetworking deadlocks in setDownloadTaskDidFinishDownloadingBlock
, when I reference self.tasks.count
:
[self setDownloadTaskDidFinishDownloadingBlock:^NSURL *(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location) {
// POINT OF THIS BLOCK: RETURNS A FILENAME URL WHERE THE DOWNLOAD SHOULD GET STORED
__strong typeof(self) strongSelf = weakS;
if( !strongSelf )
return nil;
// deadlocks here on tasks.count
DDLogVerbose(@"setDownloadTaskDidFinishDownloadingBlock, start; dataTasks - %lu, downloadTasks - %lu", (unsigned long)strongSelf.dataTaskManager.tasks.count, (unsigned long)strongSelf.tasks.count);
dataTaskManager refers to a separately subclassed AFHTTPSessionManager which is a standard session (not background) -- removing this from the logging line above does not solve the problem.
DownloadManager's completionQueue is set in init to be the same serial queue that the DownloadManager uses for everything:
_processingQueue = dispatch_queue_create([[BUNDLE_IDENTIFIER stringByAppendingString:@".BackgroundSessionManager"] cStringUsingEncoding:NSUTF8StringEncoding], NULL);
dispatch_queue_set_specific(_processingQueue, (__bridge const void *)(_processingQueue), (__bridge void *)(_processingQueue), NULL);
self.completionQueue = _processingQueue; // set AFNetworking completionQueue to be our queue
... but that doesn't seem to help.
Here's the deadlock:
(lldb) bt all
thread #1: tid = 0x137b14, 0x0000000198958e0c libsystem_kernel.dylib`mach_msg_trap + 8, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
frame #0: 0x0000000198958e0c libsystem_kernel.dylib`mach_msg_trap + 8
frame #1: 0x0000000198958c88 libsystem_kernel.dylib`mach_msg + 72
frame #2: 0x00000001864eb724 CoreFoundation`__CFRunLoopServiceMachPort + 200
frame #3: 0x00000001864e9678 CoreFoundation`__CFRunLoopRun + 940
frame #4: 0x00000001864152d4 CoreFoundation`CFRunLoopRunSpecific + 396
frame #5: 0x000000018fe736fc GraphicsServices`GSEventRunModal + 168
frame #6: 0x000000018b012f40 UIKit`UIApplicationMain + 1488
frame #7: 0x00000001000a60a4 Grab`main(argc=1, argv=0x000000016fd8ba80) + 124 at main.m:14
frame #8: 0x000000019885aa08 libdyld.dylib`start + 4
thread #2: tid = 0x137b6b, 0x0000000198958c24 libsystem_kernel.dylib`kevent64 + 8, queue = 'com.apple.libdispatch-manager'
frame #0: 0x0000000198958c24 libsystem_kernel.dylib`kevent64 + 8
frame #1: 0x0000000100496588 libdispatch.dylib`_dispatch_mgr_invoke + 276
frame #2: 0x000000010048709c libdispatch.dylib`_dispatch_mgr_thread + 52
thread #3: tid = 0x137b6c, 0x0000000198973c78 libsystem_kernel.dylib`__workq_kernreturn + 8
frame #0: 0x0000000198973c78 libsystem_kernel.dylib`__workq_kernreturn + 8
frame #1: 0x0000000198a0d2dc libsystem_pthread.dylib`_pthread_wqthread + 992
thread #6: tid = 0x137b6f, 0x0000000198973c78 libsystem_kernel.dylib`__workq_kernreturn + 8
frame #0: 0x0000000198973c78 libsystem_kernel.dylib`__workq_kernreturn + 8
frame #1: 0x0000000198a0d2dc libsystem_pthread.dylib`_pthread_wqthread + 992
* thread #7: tid = 0x137b97, 0x0000000198958e48 libsystem_kernel.dylib`semaphore_wait_trap + 8, queue = 'NSOperationQueue 0x17022f180 :: NSOperation 0x170449cf0 (QOS: LEGACY)'
frame #0: 0x0000000198958e48 libsystem_kernel.dylib`semaphore_wait_trap + 8
frame #1: 0x0000000100494544 libdispatch.dylib`_dispatch_semaphore_wait_slow + 256
* frame #2: 0x000000010018e8f4 Grab`-[AFURLSessionManager tasksForKeyPath:](self=0x0000000127d1e050, _cmd=0x0000000100203d49, keyPath=0x000000017042fdc0) + 340 at AFURLSessionManager.m:617
frame #3: 0x000000010018ee88 Grab`-[AFURLSessionManager tasks](self=0x0000000127d1e050, _cmd=0x0000000191f79785) + 76 at AFURLSessionManager.m:623
frame #4: 0x00000001000a8efc Grab`__36-[DownloadManager loadSessionBlocks]_block_invoke150(.block_descriptor=<unavailable>, session=0x0000000170132700, downloadTask=0x0000000127e408b0, location=0x00000001702bf860) + 328 at DownloadManager.m:149
frame #5: 0x0000000100193734 Grab`-[AFURLSessionManager URLSession:downloadTask:didFinishDownloadingToURL:](self=0x0000000127d1e050, _cmd=0x000000018d2b4ddd, session=0x0000000170132700, downloadTask=0x0000000127e408b0, location=0x00000001702bf860) + 292 at AFURLSessionManager.m:1082
frame #6: 0x0000000185f70b70 CFNetwork`__82-[NSURLSession delegate_downloadTask:didFinishDownloadingToURL:completionHandler:]_block_invoke + 40
frame #7: 0x000000018741b1c4 Foundation`__NSBLOCKOPERATION_IS_CALLING_OUT_TO_A_BLOCK__ + 16
frame #8: 0x000000018736c604 Foundation`-[NSBlockOperation main] + 96
frame #9: 0x000000018735c1cc Foundation`-[__NSOperationInternal _start:] + 636
frame #10: 0x000000018741df28 Foundation`__NSOQSchedule_f + 228
frame #11: 0x0000000100484f94 libdispatch.dylib`_dispatch_client_callout + 16
frame #12: 0x000000010048fdb8 libdispatch.dylib`_dispatch_queue_drain + 780
frame #13: 0x00000001004882c4 libdispatch.dylib`_dispatch_queue_invoke + 132
frame #14: 0x00000001004925d4 libdispatch.dylib`_dispatch_root_queue_drain + 772
frame #15: 0x0000000100494248 libdispatch.dylib`_dispatch_worker_thread3 + 132
frame #16: 0x0000000198a0d22c libsystem_pthread.dylib`_pthread_wqthread + 816
thread #12: tid = 0x137b9e, 0x0000000198973c78 libsystem_kernel.dylib`__workq_kernreturn + 8
frame #0: 0x0000000198973c78 libsystem_kernel.dylib`__workq_kernreturn + 8
frame #1: 0x0000000198a0d2dc libsystem_pthread.dylib`_pthread_wqthread + 992
thread #10: tid = 0x137bf9, 0x0000000198958e0c libsystem_kernel.dylib`mach_msg_trap + 8, name = 'com.apple.NSURLConnectionLoader'
frame #0: 0x0000000198958e0c libsystem_kernel.dylib`mach_msg_trap + 8
frame #1: 0x0000000198958c88 libsystem_kernel.dylib`mach_msg + 72
frame #2: 0x00000001864eb724 CoreFoundation`__CFRunLoopServiceMachPort + 200
frame #3: 0x00000001864e9678 CoreFoundation`__CFRunLoopRun + 940
frame #4: 0x00000001864152d4 CoreFoundation`CFRunLoopRunSpecific + 396
frame #5: 0x0000000185ef2594 CFNetwork`+[NSURLConnection(Loader) _resourceLoadLoop:] + 440
frame #6: 0x0000000187435db8 Foundation`__NSThread__main__ + 1072
frame #7: 0x0000000198a0fdc8 libsystem_pthread.dylib`_pthread_body + 164
frame #8: 0x0000000198a0fd24 libsystem_pthread.dylib`_pthread_start + 160
thread #11: tid = 0x137bfa, 0x0000000198973498 libsystem_kernel.dylib`__select + 8, name = 'com.apple.CFSocket.private'
frame #0: 0x0000000198973498 libsystem_kernel.dylib`__select + 8
frame #1: 0x00000001864f1128 CoreFoundation`__CFSocketManager + 672
frame #2: 0x0000000198a0fdc8 libsystem_pthread.dylib`_pthread_body + 164
frame #3: 0x0000000198a0fd24 libsystem_pthread.dylib`_pthread_start + 160
(lldb)
回答1:
.... I figured it out. Because of the way tasksForKeyPath is implemented, it is not possible to call .*tasks from the blocks like setTaskDidCompleteBlock, because the NSURLSession method underneath (getTasksWithCompletionHandler) will call back on the delegate queue, which is of course blocked by the original block like setTaskDidCompleteBlock. This sucks, but it's kinda Apple's fault I guess, for making the method that gets tasks asynchronous...
So the workaround is to avoid getting the current tasks from blocks like setTaskDidCompleteBlock (or anything that they call)
来源:https://stackoverflow.com/questions/31944465/afnetworking-deadlock-on-tasks-tasksforkeypath