崩溃问题不是作者本人遇到的哈,虽然现在没遇到,不代表以后不会遇到,而且如果真遇到这种崩溃,很难查出原因,因为根本不会想到是信息量释放引起的,所以排查此崩溃很容易走弯路,在此做下总结,以此共勉。
我们看个能复现此崩溃的demo
dispatch_semaphore_t semp = dispatch_semaphore_create(1);
dispatch_block_t block = ^{
dispatch_semaphore_signal(semp);
NSLog(@"signal");
};
NSMutableArray *array = [NSMutableArray array];
for (NSInteger i = 0; i < 4; i++) {
NSLog(@"wait");
dispatch_semaphore_wait(semp, DISPATCH_TIME_FOREVER);
if (i > 2) {//当I大于2时,只执行 wait ,没执行signal
break;
}else{ //当I小于等于2时,signal与wait是配对的
block();
}
}
控制台输出如下:
019-07-29 11:12:02.300190+0800 TEST[10987:950615] +[CATransaction synchronize] called within transaction
2019-07-29 11:12:02.439848+0800 TEST[10987:950615] wait
2019-07-29 11:12:02.439934+0800 TEST[10987:950615] signal
2019-07-29 11:12:02.439962+0800 TEST[10987:950615] wait
2019-07-29 11:12:02.439986+0800 TEST[10987:950615] signal
2019-07-29 11:12:02.440009+0800 TEST[10987:950615] wait
2019-07-29 11:12:02.440032+0800 TEST[10987:950615] signal
2019-07-29 11:12:02.440054+0800 TEST[10987:950615] wait
上面代码执行后,wait数大于signal次数,即信号量的当前值小于初始化,超过函数作用域后,会释放信号量,此时会崩溃产生
原因是信号量的销毁会调用_dispatch_semaphore_dispose函数,而此函数会执行信号当前值与初始化值的比较,如果小于初始化值,则直接抛出崩溃。
我们看下此函数的原码:
static void
_dispatch_semaphore_dispose(dispatch_semaphore_t dsema)
{
//信号量的当前值小于初始化,会发生闪退。因为信号量已经被释放了
if (dsema->dsema_value < dsema->dsema_orig) {
DISPATCH_CLIENT_CRASH(
"Semaphore/group object deallocated while in use");
}
#if USE_MACH_SEM
kern_return_t kr;
//释放信号,这个信号是dispatch_semaphore使用的信号
if (dsema->dsema_port) {
kr = semaphore_destroy(mach_task_self(), dsema->dsema_port);
DISPATCH_SEMAPHORE_VERIFY_KR(kr);
}
//释放信号,这个信号是dispatch_group使用的信号
if (dsema->dsema_waiter_port) {
kr = semaphore_destroy(mach_task_self(), dsema->dsema_waiter_port);
DISPATCH_SEMAPHORE_VERIFY_KR(kr);
}
#elif USE_POSIX_SEM
int ret = sem_destroy(&dsema->dsema_sem);
DISPATCH_SEMAPHORE_VERIFY_RET(ret);
#endif
_dispatch_dispose(dsema);
}
总结:
当信号量的当前值小于初始化,释放信号量时,会导致崩溃,简而言之就是,signal的调用次数一定要大于等于wait的调用次数,否则导致崩溃。
网友评论