我的编程空间,编程开发者的网络收藏夹
学习永远不晚

iOS中多线程的经典崩溃有哪些

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

iOS中多线程的经典崩溃有哪些

本篇内容介绍了“iOS中多线程的经典崩溃有哪些”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

Block 回调的崩溃

在MRC环境下,使用Block 来设置下载成功的图片。当self释放后,weakSelf变成野指针,接着就悲剧了

__block ViewController *weakSelf = self;
[self.imageView imageWithUrl:@"" completedBlock:^(UIImage *image, NSError *error) {
NSLog(@"%@",weakSelf.imageView.description);
}];

多线程下Setter 的崩溃

Getter & Setter 写多了,在单线程的情况下,是没有问题的。但是在多线程的情况下,可能会崩溃。因为[_imageView release]; 这段代码可能会被执行两次,oops!

UIKit 不是线程,所以在不是主线程的地方调用UIKit 的东西,有可能在开发阶段完全没问题,直接免测。但是一到线上,崩溃系统可能都是你的崩溃日志。Holy shit!

解决办法:通过hook 住setNeedsLayout,setNeedsDisplay,setNeedsDisplayInRect来检查当前调用的线程是否是主线程。

-(void)setImageView:(UIImageView *)imageView
{
if (![_imageView isEqual:imageView])
{
[_imageView release];
_imageView = [imageView retain];
}
}

更多Setter 类型的崩溃

property 的属性,写的最多的就是nonatomic,一般情况下也是没有问题的!

@interface ViewController ()
@property (strong,nonatomic) NSMutableArray *array;
@end

跑一下下面这段代码,你会看到:

malloc: error for object 0x7913d6d0: pointer being freed was not allocated

for (int i = 0; i < 100; i++) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
self.array = [[NSMutableArray alloc] init];
});
}

原因就是:对象被重复relaese 了。查看一下runtime 源码

iOS中多线程的经典崩溃有哪些

解决办法:属性声明为atomic.

一个更为常见的例子:

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

如果A,B两个线程同时访问到if语句, 此时handler == nil条件满足, 两个线程都走到下一句初始化实例.

此时A线程先完成初始化并赋值(这个实例我们叫它a), 然后继续往后走到其他逻辑.而这时候, B线程开始做初始化并赋值(这个实例我们叫它b), handler将指向B线程初始化出来的对象. 而A初始化出来的实例a因为引用计数减少1(减少到0)而被释放. 但在A线程中, 代码还会尝试访问a所在的地址, 这个地址里的内容因为被释放而变得无法预测, 从而导致野指针.

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

多线程下对变量的存取

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的判断。

作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS交流群:519832104 不管你是小白还是大牛欢迎入驻,分享经验,讨论技术,大家一起交流学习成长!

另附上一份各好友收集的大厂面试题,需要iOS开发学习资料、面试真题,可以添加iOS开发进阶交流群,进群可自行下载!

iOS中多线程的经典崩溃有哪些

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

下面截取SDWebImage源码:

dispatch_barrier_sync(self.barrierQueue, ^{
SDWebImageDownloaderOperation *operation = self.URLOperations[url];
if (!operation) {
operation = createCallback();
// ****注意这行****
self.URLOperations[url] = operation;
__weak SDWebImageDownloaderOperation *woperation = operation;
operation.completionBlock = ^{
SDWebImageDownloaderOperation *soperation = woperation;
if (!soperation) return;
if (self.URLOperations[url] == soperation) {
[self.URLOperations removeObjectForKey:url];
};
};
}
// ****注意这行****
id downloadOperationCancelToken = [operation addHandlersForProgress:progressBlock completed:completedBlock];
}

SDWebImage的下载器会根据URL做下载任务对应NSOperation映射,相同的URL会映射到同一个未执行的NSOperation。当A组图片下载完成后,相同的url 回调是 GroupB 而不是Group A。此时Group B的计数为1 。当B 组图片全部下载完后,结束计数为 5+1 。因为enter 的次数为5 ,leave 的次数为6 ,因此会崩溃!

最后一个持有者释放后的崩溃

对象A被 manager 持有,在A中调用[Manager removeObjectA]。A对象的retainCount -1, 当retainCount 等于零时,对象A已经开始释放了。在调用removeObjectA 后,紧接着调用[self doSomething] ,就会崩溃。

-(void)finishEditing
{
[Manager removeObject:self];
[self doSomething];
}

这种情况一般会发生在数组或者字典包含对象,而且是对象的最后持有者。当在对象处理不好,就会有上面的崩溃。还有一种情况就是,当数组或者字典里面的对象已经被释放了,当遍历数组或者取字典里面的值发生崩溃。这种情况,会让人很崩溃,因为有时候堆栈是这样的:

Thread 0 Crashed:
0 libobjc.A.dylib 0x00000001816ec160 _objc_release :16 (in libobjc.A.dylib)
1 libobjc.A.dylib 0x00000001816edae8 __ZN12_GLOBAL__N_119AutoreleasePoolPage3popEPv :508 (in libobjc.A.dylib)
2 CoreFoundation 0x0000000181f4c9fc __CFAutoreleasePoolPop :28 (in CoreFoundation)
3 CoreFoundation 0x0000000182022bc0 ___CFRunLoopRun :1636 (in CoreFoundation)
4 CoreFoundation 0x0000000181f4cc50 _CFRunLoopRunSpecific :384 (in CoreFoundation)
5 GraphicsServices 0x0000000183834088 _GSEventRunModal :180 (in GraphicsServices)
6 UIKit 0x0000000187236088 _UIApplicationMain :204 (in UIKit)
7 Tmall4iPhone 0x00000001000b7ae4 main main.m:50 (in Tmall4iPhone)
8 libdyld.dylib 0x0000000181aea8b8 _start :4 (in libdyld.dylib)

产生这种堆栈可能的场景是:

释放Dictionary的时候,某个值(value)因为被其他代码提前释放变成野指针, 此时再次被释放触发Crash. 如果可以在每个Dictionary释放的时候, 把所有的key/value打出来, 如果某个key/value刚好被打出来之后, crash就发生了, 那么挂就挂在刚被打出来的key/value上.

对象的释放线程要和它处理事情的线程一致

对象A在主线程监听Notification事件,如果这个对象被其它线程释放了。此刻,如果对象A 正在执行notification 相关的操作,再访问对象相关资源就野指针了,发生crash.

performSelector:withObject:afterDelay:

调用此方法,如果不是在主线程,那么必须要确保当前线程的ruuloop是存在的,performSelector_xxx_afterDelay 依赖runlopp才能执行。另外使用 performSelector:withObject:afterDelay:和 cancelPreviousPerformRequestsWithTarget组合的时候要小心。

  • afterDelay会增加receiver的引用计数,cancel则会对应减一

  • 如果在receiver的引用计数只剩下1 (仅为delay)时,调用cancel之后会立即销毁receiver,后续再调用receiver的方法就会crash

__weak typeof(self) weakSelf = self;
[NSObject cancelPreviousPerformRequestsWithTarget:self];
if (!weakSelf)
{
//NSLog(@"self被销毁");
return;
}
[self doOther];

“iOS中多线程的经典崩溃有哪些”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

iOS中多线程的经典崩溃有哪些

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

iOS中多线程的经典崩溃有哪些

本篇内容介绍了“iOS中多线程的经典崩溃有哪些”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!Block 回调的崩溃在MRC环境下,使用Blo
2023-06-04

qt程序异常崩溃的原因有哪些

Qt程序异常崩溃的原因可能有以下几种:1. 内存错误:例如访问已释放内存、越界访问数组等。2. 线程问题:例如多个线程之间的竞争条件、死锁等。3. 异常处理不当:例如未处理的异常、异常被忽略等。4. 文件操作问题:例如打开不存在的文件、无法
2023-09-22

海外多ip服务器崩溃的原因有哪些

海外多IP服务器崩溃的原因可能有以下几种:硬件故障:服务器硬件出现故障,如硬盘损坏、内存故障、CPU故障等,导致服务器崩溃。网络问题:网络连接不稳定、网络带宽不足、网络攻击等都可能导致服务器崩溃。软件问题:操作系统或应用程序出现bug或漏洞
海外多ip服务器崩溃的原因有哪些
2024-05-23

java中经典的JVM锁有哪些

本篇内容介绍了“java中经典的JVM锁有哪些”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!synchronized synchronize
2023-06-17

C语言的经典程序有哪些

本篇内容介绍了“C语言的经典程序有哪些”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!1、C语言必背18个经典程序第一个------乘法表。用
2023-07-02

linux中多进程和多线程的区别有哪些

这篇“linux中多进程和多线程的区别有哪些”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“linux中多进程和多线程的区别有
2023-06-26

多线程在Java中的用法有哪些

本篇文章给大家分享的是有关多线程在Java中的用法有哪些,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。1.创建线程在Java中创建线程有两种方法:使用Thread类和使用Run
2023-05-31

Java中多线程的中断机制有哪些

本篇文章为大家展示了Java中多线程的中断机制有哪些,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。1、interrupt()public void interrupt() { if (this !
2023-05-30

python中多线程的优缺点有哪些

本篇文章给大家分享的是有关python中多线程的优缺点有哪些,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。python的数据类型有哪些?python的数据类型:1. 数字类型,
2023-06-14

Java中多线程面试题有哪些

这篇文章将为大家详细讲解有关Java中多线程面试题有哪些,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。进程和线程进程是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的。系统运行一个程序即是从
2023-06-14

java中有哪些实现多线程的方法

这篇文章将为大家详细讲解有关java中有哪些实现多线程的方法,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。常用的java框架有哪些1.SpringMVC,Spring Web MVC是一种基
2023-06-14

Java项目中多线程的作用有哪些

这期内容当中小编将会给大家带来有关Java项目中多线程的作用有哪些,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。1.创建线程在Java中创建线程有两种方法:使用Thread类和使用Runnable接口。在
2023-05-31

python中多线程爬虫的优势有哪些

这篇文章主要介绍了python中多线程爬虫的优势有哪些,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。python是什么意思Python是一种跨平台的、具有解释性、编译性、互动
2023-06-14

Android的线程、多线程和线程池面试题有哪些

这篇“Android的线程、多线程和线程池面试题有哪些”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Android的线程、多
2023-06-04

java多线程之线程同步的方法有哪些

Java中线程同步的方法有以下几种:1. synchronized关键字:使用synchronized关键字可以实现对代码块、方法或对象的同步,确保同一时间只有一个线程可以访问被同步的代码块、方法或对象。2. ReentrantLock类:
2023-09-27

php多线程与并发线程的区别有哪些

PHP是一种脚本语言,不支持真正的多线程,但可以通过多进程和协程来实现类似多线程的并发操作。并发性:多线程是指在一个程序中同时运行多个线程,每个线程执行不同的任务。并发线程是指在一个程序中同时执行多个线程,每个线程可以是同一个任务的不同实例
2023-10-23

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录