iOS中多线程的经典崩溃总结大全

2020-01-21 03:15:45刘景俊

问题还有一个很关键的点, 在一个对象的某个方法的调用过程中, 这个对象的引用计数并不会增加, 到导致它如果被释放, 后续的执行过程中对这个对象的访问就可能会导致野指针[1].


Exception Type: SIGSEGV
Exception Codes: SEGV_ACCERR at 0x12345678
Triggered by Thread: 1

简单加个锁就可以解决问题了:


 @synchronized(self){
 if(handler == nil)
 {
  hander = [[Handler alloc] init];
 }
 }
return handler;

0x3 多线程下对变量的存取


if (self.xxx) {
 [self.dict setObject:@"ah" forKey:self.xxx];
}

大家第一眼看到这样的代码,是不是会认为是正确的?因为在设置key的时候已经提前进行了self.xxx为非nil的判断,只有非nil得情况下才会执行后续的指令。但是,如上代码只有在单线程的前提下才是正确的。

假设我们将上述代码目前执行的线程为Thread A,当我们执行完if (self.xxx)的语句之后,此时CPU将执行权切换给了Thread B,而这个时候Thread B中调用了一句self.xxx = nil。 使用局部变量可以解决这个问题


__strong id val = self.xxx;
if (val) {
 [self.dict setObject:@"ah" forKey:val];
}

这样,无论多少线程尝试对self.xxx进行修改,本质上的val都会保持现有的状态,符合非nil的判断。

0x4 dispatch_group 的崩溃

dispatch_group_enter 和 leave 必须是匹配的,不然就会crash . 在多资源下载的时候,往往需要使用多线程并发下载,全部下载完之后通知用户。开始下载,dispatch_group_enter ,下载完成dispatch_group_leave 。 非常简单的流程,但是当代码复杂到一定程度或者是使用了一些第三方库的时候,就很大可能出问题。


dispatch_group_t serviceGroup = dispatch_group_create();
dispatch_group_notify(serviceGroup, dispatch_get_main_queue(), ^{
 NSLog(@"Finish downloading :%@", downloadUrls);
});
// t 是一个包含一堆字符串的数组 
[downloadUrls enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
 dispatch_group_enter(serviceGroup);
 SDWebImageCompletionWithFinishedBlock completion =
 ^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
  dispatch_group_leave(serviceGroup);
  NSLog(@"idx:%zd",idx);
 };
 [[SDWebImageManager sharedManager] downloadImageWithURL:[NSURL URLWithString: downloadUrls[idx]]      options:SDWebImageLowPriority      progress:nil             completed:completion];
}];

使用多线程进行并发下载,直到所有图片都下载完成(可以失败)进行回调,其中图片下载使用的是SDWebImage.发生崩溃的场景是:有10 张图片,分开两次下载(A & B)。其中在B组里面有一张图片和A组下载的图片重复了。假设A组下载对应GroupA ,B组GroupB