文档章节

iOS主线程与子线程

s
 sj1910
发布于 2016/06/18 20:43
字数 1876
阅读 172
收藏 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
08/26
0
0
iOS按钮倒计时在进入后台不继续计时的处理

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

云上飞飞
07/06
0
0
iOS Debuger(便捷辅助调试器)

前言 首先写这篇文章之前祝大家周末愉快,然后自我介绍一下,我叫吴海超(WHC)在iOS领域有丰富的开发架构经验Github以后我也会以文章的形式分享具有实战意义的文章给大家,希望能够给大家有所...

WHC
10/30
0
0
IOS NSNotification 通知

一. 先看下官方对NSNotification通知的解释 1. NSNotification 通知  @interface NSNotification : NSObject   接口通知,继承NSObject,实现NSCopying,NSCoding协议  A container for...

冬季的暖风
08/02
0
0

没有更多内容

加载失败,请刷新页面

加载更多

手写tomcat+servlet

写程序一定要有思路,思路很重要! 一、我们分两步第一步先实现手写tomcat,第二部写servlet 所用技术: 1、soket通信 IO流 2、http请求与相应 3、解析xml 4、java反射技术 导入所需要的jar...

jason_kiss
30分钟前
1
0
Beetl模板的基础用法 【变量、循环、条件】---《Beetl视频课程》(2)

本期视频做了一个博客的首页列表; 内容简介:springboot 集成 beetlsql;使用for循环,使用if控制语句,使用虚拟属性,定义变量等等 一起学beetl目录:https://my.oschina.net/u/1590490?ta...

Gavin-King
35分钟前
1
0
各种视频监控上墙方案的比较

方案1、一使用 DVR 、NVR 直接显示上墙 不得不说,这种办法是成本最低廉的,但这里有不少限制: 无法实现分散点的集中上墙。譬如连锁经营的酒店,如果我在总部建立一个集中上墙的环境,这个就...

PeakFang-BOK
59分钟前
4
0
netfilter 和 iptables

一. netfilter 1. 什么是entfilter 和 iptables netfilter指整个项目名 在这个项目里面,netfilter特指内核中的netfilter框架, iptables指用户空间的配置工具。 netfilter在协议栈中添加了5...

Fc丶
今天
2
0
搞定了微信小程序富文本渲染解决方案-后端渲染方案Html2Wxml2J

先介绍一下最近遇到的问题: 最近小程序项目中有文章详情页需要渲染富文本,微信小程序官方提供的<rich-text>是个弱鸡,很多标签不支持,用起来也麻烦,性能也不咋地。 吐槽完了,我们决定寻...

山东-小木
今天
35
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部