文档章节

iOS主线程与子线程

s
 sj1910
发布于 2016/06/18 20:43
字数 1876
阅读 186
收藏 1

一、什么是线程
    主线程: 当我们应用程序运行的时候,系统会自动为我们创建出来一个线程,这个线程交做主线程。
    子线程:程序员用代码手动开始的线程叫做子线程
    线程存在的意义:我们需要把比较耗时的任务,放到子线程进行操作
    **查看所在线程: NSLog(@"所在线程 ===== %d",[NSThread isMainThread]);
    输出结果: 1.主线程 ;0.子线程

二、开辟子线程的三种方法
(一).NSThread
  1.创建
- (void)aa:(UIButton *)btn
{
     threadAction方法里的代码是在子线程中执行的
     object:是想要传到方法中的参数
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadAction:) object:self.view];
    手动开启子线程
    [thread start];
    线程里面的任务执行完毕以后,系统会自动把该子线程销毁掉
}
- (void)threadAction:(UIView *)tempView {    
     for (int i = 0; i < 10000; i++) {
     // NSLog(@"%d",i);
    }
注意:
     在子线程里面杜绝刷新UI界面
   //tempView.backgroundColor = [UIColor redColor];
    子线程执行完毕之后,回到主线程
    [self performSelectorOnMainThread:@selector(mainThreadAction:) withObject:tempView waitUntilDone:YES];
}
- (void)viewDidLoad {
    [super viewDidLoad];
注意:在[super viewDidLoad];下面这样写法是错误的,
 self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadAction:) object:self.view];  
  错误原因:
    1.执行完毕后,线程自动销毁,重新执行找不到子线程
    2.执行任务当中,再次开启任务,此时该线程正处于执行的忙碌状态,如果再次开启任务,同样会程序崩溃
    解决办法:如果处于执行状态 或者完成状态,则开启一个新的对象
    // 1.利用NSThread 对象方法
if (self.thread.isExecuting | self.thread.isFinished) {
//      self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadAction:) object:self.view];
    }
    // 2.利用NSThread 类方法
    [NSThread detachNewThreadSelector:@selector(threadAction:) toTarget:self withObject:self.view];
    // 3.利用NSObject分类
   [self performSelectorInBackground:@selector(threadAction:) withObject:self.view];或
   //[self.thread performSelectorInBackground:@selector(start) withObject:self.view];
(二).NSOperation
1.NSOperation 是一个抽象类,一般不直接使用该类,而是使用其子类
1>.NSInvocationOperation 是以target action 的方式添加线程执行任务
2>.NSBlockOperation 是以block 回调执行任务
注意:上面这两个类的对象,如果单度使用,任务都是在主线程执行,如果放到**操作队列**中,任务则会在子线程执行
- (void)aa:(UIButton *)btn {
 NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(op1) object:nil];
[op1 start];
NSBlockOperation *blockOP = [NSBlockOperation blockOperationWithBlock:^{
      
    }];
此时是单独使用,是在主线程,且需要手动开启
// 操作队列:
    // 目的:是将操作放到队列中执行任务
    // 任务:在子线程还是主线程,取决于操作队列的初始化方式
    // NSOperationQueue mainQueue 主线程
    // [NSOperationQueue alloc] init 子线程
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(op2) object:nil];
// 队列添加操作 无需再调用 [op1 start]; 方法
    // 谁先被添加,谁先被执行,但是执行速度可能有快有慢,但执行顺序永远是先添加的op1先执行,op1 一开始,op2马上开始,无需等待op1结束
    // 线程资源重复利用:op1开启一个线程,op2的时候,会看op1的任务是否执行完毕,如果执行完毕,则会占用op1线程资源执行任务,不再开启新线程,如果op1没有完成,op2则开辟新线程
    // 设置操作队列最大线程数
    [queue setMaxConcurrentOperationCount:1];
    [queue addOperation:op1];
    [queue addOperation:op2];
    [queue addOperation:blockOP];
}

(三)、GCD

这是我们现在开发中最常用的方法
GCD grand central dispatch 宏观中央调度中心(多线程计数优化)
     GCD两个重要概念
     1.队列:
       >1.串行队列
         一、系统主队列
         二、自己创建的串行队列
       >2.并行队列
         一、系统全局队列
         二、自己创建的并行对列
     2.任务:
       >1.同步任务
       >2.异步任务
     任务和队列之间的关系
     1.队列需要存放任务
     2.任务需要到队列中执行
     异步/同步 任务可以放到串行队列,也可以放到并行队列,但是任务是在主线程还是子线程执行,任务的方式是同步还是异步,取决于任务自身以及所在的队列

#pragma mark --- 系统主队列 ---
- (IBAction)mainQueue:(id)sender {
    // 获取系统主队列(串行队列)
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
  // 在系统主队列中添加异步任务,任务是在主线程中执行的,任务的执行方式是同步执行,一个任务开始,第二个任务不会马上开始,需等到第一个任务结束
    // 第一个参数:队列
    // 第二个参数:block任务里面执行的代码
     // 添加任务 async异步任务 sync同步任务
    dispatch_async(mainQueue, ^{
       
    });
    dispatch_async(mainQueue, ^{
      
    });   
    // 经典错误
    // 在主队列中添加同步任务,这个任务需要等上一个任务结束才会执行,而上一个任务(- (void)viewDidLoad {},互相嵌套)永远不会结束,造成两个任务之间互相等待的现象,造成界面真死
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    dispatch_sync(mainQueue, ^{
    
    });
}
#pragma makr --- 自己创建串行队列 ---
- (IBAction)seraialQueue:(id)sender {
    // 自己创建串行队列
    // 第一个参数:队列的标示
    // 第二个参数:决定这个队列是串行队列还是并行队列
    // DISPATCH_QUEUE_SERIAL 串行队列
    // DISPATCH_QUEUE_CONCURRENT 并行队列
    // 在自己创建的串行队列中,添加的异步任务,任务是在子线程执行,任务执行方式是同步的
    dispatch_queue_t serialQueue = dispatch_queue_create(nil, DISPATCH_QUEUE_SERIAL);
    /*
    dispatch_async(serialQueue, ^{
     
    });
    dispatch_async(serialQueue, ^{
     
    });
    */
    在自己创建的串行队列中,添加的同步任务,任务是在主线程执行,任务执行方式是同步的
    dispatch_sync(serialQueue, ^{
    
    });
}
#pragma makr --- 自己创建并行队列 ---
- (IBAction)concurrent:(id)sender {
    // 创建并行队列
    dispatch_queue_t serialQueue = dispatch_queue_create(nil, DISPATCH_QUEUE_CONCURRENT);
     在自己创建的并行队列中,添加的异步任务,任务是在子线程执行,任务执行方式是异步的
    /*
    dispatch_async(serialQueue, ^{
   
    });
    dispatch_async(serialQueue, ^{
 
    });
*/
   // 在自己创建的并行队列中,添加的同步任务,任务是在主线程执行,任务执行方式是同步的
    dispatch_sync(serialQueue, ^{

    });
    dispatch_sync(serialQueue, ^{
   
    });    
}
#pragma makr --- 系统并行(全局)队列 ---
- (IBAction)glabalQueue:(id)sender {
    // 获取全局队列
    // 第一个参数:队列优先级,分为:高 低 默认 后台(默认default)
    dispatch_queue_t gloablaQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    在系统全局队列中添加异步任务,任务是在子线程,执行任务方式异步
    /*
    dispatch_async(gloablaQueue, ^{
     
    });
    dispatch_async(gloablaQueue, ^{
     
    });
*/
     在系统全局队列中添加同步任务,任务是在主线程,执行任务方式同步
    dispatch_sync(gloablaQueue, ^{
    
    });
    dispatch_sync(gloablaQueue, ^{

    });
}
#pragma makr --- 只执行一次 ---
这个在现实开发中用到最多
    // 单例的写法变成使用GCD线程安全锁的形式
    // 只允许一个线程访问该资源,其余的都会被拒绝
    static ViewController *vc = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
vc = [[ViewContrlooer alloc] init];
只会执行一次
    });
     //   return vc;
#pragma makr --- 通信 ---
    // 所谓线程通信:就是在子线程中执行耗时的任务,执行完毕后,回到主线程中刷新UI
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),^{
     执行耗时任务
dispatch_async(dispatch_get_main_queue(),^{
   刷新UI
 });
#pragma makr --- 延迟- - list text here ---
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(DISPATCH_TIME_NOW * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    });
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{
    });
备注:在WWDC 2013中,Apple的团队对NSURLConnection进行了重构,并推出了NSURLSession作为替代作为iOS7中新的网络接口,可以异步请求后,线程也逐渐被弱化,虽然工作中不常会遇到,但作为开发人员遇到还是要会使用。

© 著作权归作者所有

共有 人打赏支持
s
粉丝 0
博文 4
码字总数 9171
作品 0
南京
程序员
私信 提问
iOS查看屏幕帧数工具--YYFPSLabel

学习 YYKit 代码时,发现 ibireme 在项目里加入的一个查看当前屏幕帧数的小工具,效果如下: 挺实用,实现方法也很简单,但是思路特别棒。 这里是Demo: YYFPSLabel 这里我把这个小工具从 中...

yehot
2016/04/05
0
0
关于iOS多线程,这边勉强可以看看(OC&Swift)

iOS开发多线程总是绕不过的坎,看了很多前辈们优秀的文章,如:关于iOS多线程,我说,你听,没准你就懂了!、谈iOS多线程(NSThread、NSOperation、GCD)编程、iOS多线程:『GCD』详尽总结、i...

Andy_Ron
2018/08/26
0
0
【读书笔记】iOS-使用GCD改善性能

一,队列简介。 有些与并行处理相关的术语令人迷惑。线程是一个常用的术语,在iOS应用中,线程是标准的POSIX线程。从技术上说,线程不过是一组指令,可在进程中独立地处理;在同一个进程中,...

菜鸟and小白
2018/12/13
0
0
2018 iOS 面试题大全(补充完整版)

原文地址:2018 iOS 面试题大全 由于原作者并没有继续更新,这里我转过来继续更新下 这个栏目将持续更新--请iOS的小伙伴关注! 1、iOS 应用导航模式有哪些? 2、iOS 中持久化方式有哪些? 3、...

Theendisthebegi
2018/11/15
0
0
iOS按钮倒计时在进入后台不继续计时的处理

iOS程序进入后台后十分钟之内就会被系统kill掉,我想要程序进入后台后仍然运行计时功能,怎么解决呢? 方法一:可以使用记录开始时间和获取当前时间的时间差进行处理 还是直接上代码: 下面的...

云上飞飞
2018/07/06
0
0

没有更多内容

加载失败,请刷新页面

加载更多

SQL语句查询

1.1 排序 通过order by语句,可以将查询出的结果进行排序。放置在select语句的最后。 格式: SELECT * FROM 表名 ORDER BY 排序字段ASC|DESC; ASC 升序 (默认) DESC 降序 1.查询所有商品信息,...

stars永恒
39分钟前
2
0
IntelliJ IDEA 第一个 Scala 程序

IntelliJ 安装完成 Scala 插件后,你需要尝试使用 IntelliJ 来创建并且运行第一个程序。 通常这个程序只是简单的输出 Hello World。 创建一个新工程 在文件下面选择新建,然后选择创建工程。...

honeymose
44分钟前
2
0
mysql分表,分区的区别和联系

一,什么是mysql分表,分区 什么是分表,从表面意思上看呢,就是把一张表分成N多个小表,具体请看mysql分表的3种方法 什么是分区,分区呢就是把一张表的数据分成N多个区块,这些区块可以在同...

吴伟祥
46分钟前
1
0
csapp 习题 - 如何实现异或 exclusive-or

阅读 csapp v3 时,练习题 2.13 很有意思。练习题描述如下。 位设置是对于参数 mask 中每一个为 1 的位,那么参数 x 中相应位则被设置为 1 ;位清除是对于参数 mask 中每一个为 1 的位,那么...

ylme
昨天
6
0
Amino——产品迭代

兴趣部落产品迭代 时间 版本号 更新内容 备注 2019年1月2日 v3.1.1 支持定制部落首页的内容tab,酋长可以将精华、相册、分类添加到部落首页啦。 支持申请酋长,酋长可以直接推送优质话题,快...

铸剑为犁413
昨天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部