Objective-C中dispatch_async触发EXC_BAD_ACCESS崩溃问题求助
相关代码
- (void)stopCurrentSessionFromSuite:(NSNotification*)notification{ isCloseCurrentSession = YES; dispatch_async(self.dataSendingQueue, ^{ CARD8 messageToDisableSession = rpcHostDisableMsg; [readerNwriter writeExact:(char*)&messageToDisableSession ofLenght:sizeof(CARD8) secApplied:NO]; }); [rpcCaptureMgr stopCaptureSession]; }
崩溃信息
系统唤醒时长:1400秒
系统完整性保护:已启用
崩溃线程:0 调度队列:com.apple.main-thread
异常类型:EXC_BAD_ACCESS (SIGSEGV) 异常代码:
KERN_INVALID_ADDRESS at 0x0000000000000070 异常说明:
EXC_CORPSE_NOTIFY
终止信号:段错误:11 终止原因:
命名空间 SIGNAL, 代码 0xb 终止进程: 异常处理程序 [0]
0x70附近的VM区域:--> __TEXT 000000010871a000-000000010873a000 [ 128K] r-x/rwx SM=COW /Applications/RemotePCSuite/*/RemotePCDesktop.app/Contents/MacOS/RemotePCDesktop
线程0崩溃:: 调度队列: com.apple.main-thread 0
libdispatch.dylib 0x00007fff72de50db _dispatch_continuation_async + 5
1 com.prosoftnet.remotepcDesktop 0x0000000108727fd5 -[RPCDesktopAppDelegate stopCurrentSessionFromSuite:] + 104
2 com.apple.CoreFoundation 0x00007fff4b7a333c CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER + 12
3 com.apple.CoreFoundation 0x00007fff4b7a32cf _CFXRegistrationPost_block_invoke + 63
4 com.apple.CoreFoundation 0x00007fff4b7ab92c __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK + 12
5 com.apple.CoreFoundation 0x00007fff4b78e8a3 __CFRunLoopDoBlocks + 275
6 com.apple.CoreFoundation 0x00007fff4b78e668 __CFRunLoopRun + 3128
7 com.apple.CoreFoundation 0x00007fff4b78d797 CFRunLoopRunSpecific + 487
8 com.apple.HIToolbox 0x00007fff4aaa2866 RunCurrentEventLoopInMode + 286
9 com.apple.HIToolbox 0x00007fff4aaa25d6 ReceiveNextEventCommon + 613
10 com.apple.HIToolbox 0x00007fff4aaa2354 _BlockUntilNextEventMatchingListInModeWithFilter + 64
11 com.apple.AppKit 0x00007fff48d9fa23 _DPSNextEvent + 2085
12 com.apple.AppKit 0x00007fff49534e6c -[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 3044
13 com.apple.AppKit 0x00007fff48d94831 -[NSApplication run] + 764
14 com.apple.AppKit 0x00007fff48d639d2 NSApplicationMain + 804
15 libdyld.dylib 0x00007fff72e0c145 start + 1
崩溃原因拆解
从堆栈和错误地址来看,这个崩溃的核心原因是传入dispatch_async的队列对象(self.dataSendingQueue)已经变成了野指针,或者self本身已经被提前释放,导致访问self.dataSendingQueue时踩了无效内存:
- 错误地址
0x0000000000000070是空指针偏移出来的,说明我们尝试访问的对象已经被系统回收了; - 崩溃发生在
_dispatch_continuation_async这一步,说明GCD在尝试处理我们传入的队列时,发现队列是无效的(已释放),直接触发了段错误。
另外还要注意,block里直接访问readerNwriter也可能有同样的风险——如果这个对象是self的属性,那当self被释放后,它也会变成野指针。
具体解决方案
针对这些问题,我们可以分几步来修复:
1. 确保队列的生命周期正确
首先检查dataSendingQueue的声明,必须用强引用(strong)来持有:
@property (nonatomic, strong) dispatch_queue_t dataSendingQueue;
然后在类的初始化方法(比如init或者awakeFromNib)中创建队列,保证它的生命周期和当前对象一致:
- (instancetype)init { self = [super init]; if (self) { _dataSendingQueue = dispatch_queue_create("com.yourcompany.remotepc.dataSending", DISPATCH_QUEUE_SERIAL); } return self; }
ARC环境下不需要手动释放GCD队列,所以不要调用dispatch_release这类方法。
2. 用弱引用-强引用避免self提前释放
在异步block中,我们需要确保self不会被提前释放,同时避免循环引用,所以用弱引用包裹self,在block内部再转成强引用做检查:
- (void)stopCurrentSessionFromSuite:(NSNotification*)notification{ isCloseCurrentSession = YES; __weak typeof(self) weakSelf = self; dispatch_async(weakSelf.dataSendingQueue, ^{ __strong typeof(weakSelf) strongSelf = weakSelf; if (!strongSelf) return; // 如果self已经释放,直接终止任务 CARD8 messageToDisableSession = rpcHostDisableMsg; [strongSelf.readerNwriter writeExact:(char*)&messageToDisableSession ofLenght:sizeof(CARD8) secApplied:NO]; }); [rpcCaptureMgr stopCaptureSession]; }
这样即使self在异步任务执行前被释放,block里也会直接返回,不会访问无效内存。
3. 增加队列的空值检查
在调用dispatch_async之前,先确认队列是否有效,避免传入nil或者野指针:
- (void)stopCurrentSessionFromSuite:(NSNotification*)notification{ isCloseCurrentSession = YES; if (!self.dataSendingQueue) { NSLog(@"Warning: dataSendingQueue is invalid, skipping async task"); [rpcCaptureMgr stopCaptureSession]; return; } __weak typeof(self) weakSelf = self; dispatch_async(self.dataSendingQueue, ^{ __strong typeof(weakSelf) strongSelf = weakSelf; if (!strongSelf) return; CARD8 messageToDisableSession = rpcHostDisableMsg; [strongSelf.readerNwriter writeExact:(char*)&messageToDisableSession ofLenght:sizeof(CARD8) secApplied:NO]; }); [rpcCaptureMgr stopCaptureSession]; }
内容的提问来源于stack exchange,提问作者Raghavendra

