dispatch_semaphore_t 造成 EXC_BAD_INSTRUCTION 崩溃

我们先来看个简单的例子:

代码1

  NSMutableArray *array = [NSMutableArray array];
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
    for (int i = 0; i < 4; i++) {
        NSString *str = [NSString stringWithFormat:@"%d-signal", i];
        [array addObject:str];
    }
    void (^block)(NSString *text) = ^(NSString *text) {
        NSLog(@"%@", text);
        dispatch_semaphore_signal(semaphore);
    };
    dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^{
        [array enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
            if(idx > 1) {
                *stop = YES;
            } else {
                block(obj);
            }
            NSLog(@"wait------");
            
        }];
    });

运行结果日志:

2019-07-26 10:09:19.895115+0800 CCNetwork[97572:22432685] 0-signal
2019-07-26 10:09:19.895227+0800 CCNetwork[97572:22432685] wait------
2019-07-26 10:09:19.895316+0800 CCNetwork[97572:22432685] 1-signal
2019-07-26 10:09:19.895421+0800 CCNetwork[97572:22432685] wait------
2019-07-26 10:09:19.895515+0800 CCNetwork[97572:22432685] wait------

咋一看感觉没什么问题,但是如果去运行下,你会发现崩溃了。!

dispatch_semaphore_t 造成 EXC_BAD_INSTRUCTION 崩溃_第1张图片
crash.png

提示:Thread 4: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0 错误,断点断在了_ destroy_helper_block。如果换个复杂的场景大家可能会把如果把重心放在_ destroy_helper_block 上去寻找答案,会发现这条路非常崎岖,很难找到想要的答案。其实这里的问题所在是在_dispatch_semaphore_dispose上。

接下来我们把代码修改下

代码2

  NSMutableArray *array = [NSMutableArray array];
    __block dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    for (int i = 0; i < 4; i++) {
        NSString *str = [NSString stringWithFormat:@"%d-signal", i];
        [array addObject:str];
    }
    void (^block)(NSString *text) = ^(NSString *text) {
        NSLog(@"%@", text);
        dispatch_semaphore_signal(semaphore);
    };
    dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^{
        [array enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            
            if(idx > 1) {
                *stop = YES;
                dispatch_semaphore_signal(semaphore);
                NSLog(@"%@", obj);
            } else {
                block(obj);
            }
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
            NSLog(@"wait------");
        }];
    });

运行结果日志:

2019-07-26 11:41:13.759351+0800 CCNetwork[99872:22495958] 0-signal
2019-07-26 11:41:13.759495+0800 CCNetwork[99872:22495958] wait------
2019-07-26 11:41:13.759579+0800 CCNetwork[99872:22495958] 1-signal
2019-07-26 11:41:13.759657+0800 CCNetwork[99872:22495958] wait------
2019-07-26 11:41:13.759734+0800 CCNetwork[99872:22495958] 2-signal
2019-07-26 11:41:13.759810+0800 CCNetwork[99872:22495958] wait------

经过简单的修改后,代码可以正常运行无误了。

比对下前后两段代码和打印的日志可以看出:
  • 代码1,初始信号量为1,代码执行了2次dispatch_semaphore_signal,3次的dispatch_semaphore_wait。所以在代码运行结束前信号量为0.
  • 代码2,初始信号量为0,代码执行了3次的dispatch_semaphore_signal,3次的dispatch_semaphore_wait。所以在代码运行结束前信号量也是为0.
推论:释放dispatch_semaphore_t调用_dispatch_semaphore_dispose 时,当前信号量值必须等于初始信号量值时能够正常运行,那么当前信号量的值大于初始信号量值呢?

我们把代码2修改下:
代码3:

 NSMutableArray *array = [NSMutableArray array];
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    for (int i = 0; i < 4; i++) {
        NSString *str = [NSString stringWithFormat:@"%d-signal", i];
        [array addObject:str];
    }
    void (^block)(NSString *text) = ^(NSString *text) {
        NSLog(@"%@", text);
        dispatch_semaphore_signal(semaphore);
    };
    dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^{
        [array enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            
            if(idx > 1) {
                *stop = YES;
                dispatch_semaphore_signal(semaphore);
                NSLog(@"%@", obj);
            } else {
                block(obj);
            }
            NSLog(@"wait------");
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        }];
      dispatch_semaphore_signal(semaphore);
      NSLog(@"3-signal");
    });

运行结果日志:

2019-07-26 11:53:10.869757+0800 CCNetwork[168:22503815] 0-signal
2019-07-26 11:53:10.869917+0800 CCNetwork[168:22503815] wait------
2019-07-26 11:53:10.870023+0800 CCNetwork[168:22503815] 1-signal
2019-07-26 11:53:10.870115+0800 CCNetwork[168:22503815] wait------
2019-07-26 11:53:10.870211+0800 CCNetwork[168:22503815] 2-signal
2019-07-26 11:53:10.870295+0800 CCNetwork[168:22503815] wait------
2019-07-26 11:53:10.870381+0800 CCNetwork[168:22503815] 3-signal

代码3能够正常运行,不会引起崩溃。由运行结果可以看出,代码3,初始信号量为0,代码执行了4次的dispatch_semaphore_signal,3次的dispatch_semaphore_wait。所以在代码运行结束前信号量也是为1.

所以我们可以得到一个结论:

dispatch_semaphore_t调用_dispatch_semaphore_dispose 释放时,当前信号量值必须大于等于初始信号量值时,才能正常释放,否则将引起EXC_BAD_INSTRUCTION指令错误。

拓展

  • 另外在dispatch_semaphore_t 处于wait状态时,释放这个dispatch_semaphore_t的引用(sema = nil),也会导致崩溃具体可以参考跳转链接
  • 遇到的 dispatch_group_t 类似错误崩溃时也可以借鉴dispatch_semaphore_t崩溃原因进行联想,这里就不进行阐述了

你可能感兴趣的:(dispatch_semaphore_t 造成 EXC_BAD_INSTRUCTION 崩溃)