Thread 1: EXC_BAD_ACCESS (code=EXC_I386_GPFLT)的崩溃原因

崩溃场景

在学习iOS多线程的时候,编写了一个demo:

dispatch_queue_t queue = dispatch_queue_create("gcd_test_label", DISPATCH_QUEUE_CONCURRENT);
    [queue addObserver:self forKeyPath:@"isExecuting" options:NSKeyValueObservingOptionNew context:nil];
    //随机创建1w个数字,输出其中最大的数字
    void (^block)(void) = ^() {
        NSMutableArray *numbers = [NSMutableArray new];
        int i = 0;
        while (i < 100) {
            int r = arc4random() % 10000;
            [numbers addObject:[NSNumber numberWithInt:r]];
            i++;
        }
        //FindMaxNumberThread是一个NSThread,不需要关注。
        FindMaxNumberThread *thread = [[FindMaxNumberThread alloc] initWithNumbers:numbers];
        [thread addObserver:self forKeyPath:@"result" options:NSKeyValueObservingOptionNew context:nil];
        [thread start];
    };
    dispatch_async(queue, block);
    dispatch_async(queue, block);
    dispatch_async(queue, block);
    dispatch_async(queue, block);

这段代码是在VC的viewDidLoad中执行的,在运行这段代码时,app直接崩溃白屏了,错误指向了dispatch_async(queue, block);这一行语句。Thread 1: EXC_BAD_ACCESS (code=EXC_I386_GPFLT)的崩溃原因_第1张图片

原因分析

百度了一下触发这个错误的原因可能是调用了僵尸对象。僵尸对象是指那些已经被释放的对象。

再看一下dispatch_async这个方法的描述:
Thread 1: EXC_BAD_ACCESS (code=EXC_I386_GPFLT)的崩溃原因_第2张图片
从描述我们可以知道:

  • queue是被系统持有的,直到block执行完成。
  • dispatch_async这个方法会帮助调用者对block进行copy和release,所以不用担心block被释放了导致在执行的时候访问到僵尸对象。

queue和block都不可能是僵尸对象。

最后怀疑的对象是这一句:

[queue addObserver:self forKeyPath:@"isExecuting" options:NSKeyValueObservingOptionNew context:nil];

Thread 1: EXC_BAD_ACCESS (code=EXC_I386_GPFLT)的崩溃原因_第3张图片

结论

queue本身应该是一个C的结构体,对queue进行KVO,会将queue转为新的类NSKVONotifying_OS_dispatch_queue_concurrent。

在试图调用queue的addObserver方法的时候,就会出错,原因就是queue是一个结构体;结构体的方法调用方式与OC类不一样。

在出现EXC_BAD_ACCESS这个错误的时候,要注意是否使用了OC的方法调用方式来调用结构体。

你可能感兴趣的:(iOS)