iOS主线程与子线程
博客专区 > sj1910 的博客 > 博客详情
iOS主线程与子线程
sj1910 发表于2年前
iOS主线程与子线程
  • 发表于 2年前
  • 阅读 98
  • 收藏 1
  • 点赞 0
  • 评论 0
摘要: 多线程是iOS开发中一个比较重要问题,在执行比较耗时的任务,我们会手动开辟子线程,放到子线程中执行

一、什么是线程
    主线程: 当我们应用程序运行的时候,系统会自动为我们创建出来一个线程,这个线程交做主线程。
    子线程:程序员用代码手动开始的线程叫做子线程
    线程存在的意义:我们需要把比较耗时的任务,放到子线程进行操作
    **查看所在线程: 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中新的网络接口,可以异步请求后,线程也逐渐被弱化,虽然工作中不常会遇到,但作为开发人员遇到还是要会使用。

共有 人打赏支持
粉丝 0
博文 4
码字总数 9171
×
sj1910
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: