zoukankan      html  css  js  c++  java
  • IOS开发之多线程NSThread

    一、什么是多线程

    NSThread是轻量级的多线程开发,使用并不复杂,但使用NSThread需要自己管理线程的生命周期,这篇文章主要讲NSThread的使用
    了解进程与线程

    1. 线程是CPU执行任务的基本单位,一个进程能有多个线程,但同时只能执行一个任务

     2. 进程就是运行中的软件,是动态的

     3. 一个操作系统可以对应多个进程,一个进程可以有多条线程,但至少有一个线程

     4. 同一个进程内的线程共享进程里的资源

     2. 主线程

     1. 进程一启动就自动创建

     2. 显示和刷新UI界面

     3. 处理UI事件

     3. 子线程

     1. 处理耗时的操作

     2. 子线程不能用来刷新UI



    NSThread常用方法

    • 使用NSThread开辟线程的两种方式:
      1. 创建并手动开启线程
        
        NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(compete) object:nil];   
        [thread start];
      2. 创建并自动开启线程
        [NSThread detachNewThreadSelector:@selector(compete) toTarget:self withObject:nil];
        • 判断当前进程是否是多线程

          BOOL isMultiThread = [NSThread isMultiThreaded];
        • 获取当前线程对象

          //输出当前线程的信息
          NSLog(@"当前所在的线程=%@",[NSThread currentThread]);

          控制台的信息

        • NSThread[10433:1182963] viewDidLoad 方法所在的线程=<NSThread: 0x7fca98507b10>{number = 1, name = main}

          number = 1 : 线程的编号,由系统设置,主线程的编号为1
          name = main:指当前所在的线程的名字叫做main,可以自己设置,主线程的名字默认是main,其他线程如果不给他设置名字默认是nil。
        • 使当前线程睡眠指定的时间,单位为秒
              1. //这句代码在哪个线程执行就让哪个线程睡眠。
                [NSThread sleepForTimeInterval:2];

                线程一旦休眠就进入阻塞状态,就是正在运行的线程没有运行结束,暂时让出CPU,这时其他处于就绪状态的线程就可以获得CPU时间,进入运行状态。

              2. 设置线程的优先级

                 //取值范围(0.0~1.0),默认为0.5,取值越大,优先级越大。
                 thread.threadPriority = 1.0;
              3. 判断当前线程是否为主线程

                [NSThread isMainThread];
              4. 给线程设置名字

                [thread setName:@"线程名字"];
              5. NSThread对象可知的三种状态

                isExecuting:是否正在执行,只读
                isFinished:是否已经完成,只读
                isCancellled:是否已经取消,可通过[thread cancel]手动设置,线程取消意味着该线程处于准备退出状态,但不会影响线程的运行。

              6. 退出当前线程

                1. //线程退出前,必须要要,该线程之后的代码将不在执行
                  if (thread.isCancelled == YES) {
                      [NSThread exit];
                   }
              7. 实例  加载一张图片      使用多线程加载一张url图片
                1. 在self.view上放一个UIImageView试图
                2. 开辟一条子线程
                3. 子线程中将url图片转成image对象
                4. 回到主线程
                5. 在主线程中将image对象给UIImageView视图

                6. #import "ViewController.h"
                  
                  #define kUrl @"http://store.storeimages.cdn-apple.com/8748/as-images.apple.com/is/image/AppleInc/aos/published/images/s/38/s38ga/rdgd/s38ga-rdgd-sel-201601?wid=848&hei=848&fmt=jpeg&qlt=80&op_sharpen=0&resMode=bicub&op_usm=0.5,0.5,0,0&iccEmbed=0&layer=comp&.v=1454777389943"
                  
                  @interface ViewController ()
                  {
                      UIImageView *imageView;
                  }
                  
                  @end
                  
                  @implementation ViewController
                  
                  - (void)viewDidLoad {
                      [super viewDidLoad];
                  
                      /*
                       * 1、在self.view上放一个UIImageView试图
                       */
                      imageView = [[UIImageView alloc]initWithFrame:CGRectMake(50, 50, 200, 200)];
                      [self.view addSubview:imageView];
                  
                      /*
                       * 2、 开辟一条子线程(我这里采用创建并手动开启线程的方式)
                  
                       * target: 信息发送者
                  
                       * selector: 方法选择器选择一个方法
                  
                       * object: 如果上面选择的方法有参数,则object便是这个方法的参数
                  
                       */
                      NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(downloadImage:) object:kUrl];
                  
                      //给线程起名字
                      thread.name = @"子线程";
                  
                      // 开启线程
                      [thread start];
                  
                  }
                  
                  /*
                   * 3、 在`子线程`中将url图片转成image对象
                  
                   *  downloadImage该方法的参数取决于创建线程时传给object的参数
                  
                   */
                  - (void)downloadImage:(NSString *)url{
                  
                      //将图片的url地址转化为data对象
                      NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:kUrl]];
                  
                      //将data对象转化为image对象
                      UIImage *image = [UIImage imageWithData:data];
                  
                      /* 
                       * 4. 是NSObject的一个方法,用来回到主线程
                  
                       * 方法updataUI将在主线程中执行
                  
                       * withObject:updateUI的参数
                  
                       * waitUntilDone: 设为YES,会阻塞当前子线程,去主线程执行updateUI方法,也就是更新UI,直到UI更新完毕。设为NO,意味着在主线程updateUI方法执行到一半时可能会被打断去做其他线程的工作,也就是说我主线程的UI还没有显示完就程序就跳出了主线程。
                       */
                      [self performSelectorOnMainThread:@selector(updateUI:) withObject:image waitUntilDone:YES];
                  
                      /*
                  
                       * 查看打印结果
                  
                       * number = 1 :线程的编号,由系统设置,主线程的编号为1
                  
                       * name = main:指当前所在的线程的名字叫做main,可以自己设置,主线程的名字默认是main,其他线程如果不给他设置名字默认是nil
                  
                       */
                      NSLog(@"downlaodImage方法所在的线程 = %@",[NSThread currentThread]);
                  
                              }
                  
                  /*
                   * 5、 在主线程中将image对象给UIImageView试图
                   */
                  
                  - (void)updateUI:(UIImage *)image{
                  
                      imageView.image = image;
                  
                      NSLog(@"downlaodImage方法所在的线程 = %@",[NSThread currentThread]);
                  
                  }
                  
                  @end

                  使用多线程加载多张图片

                    1. 在self.view上放多个UIImageView视图
                    2. 开辟多条子线程
                    3. 子线程中将url图片转成image对象
                    4. 回到主线程
                    5. 在主线程中将image对象给UIImageView视图

                    6. #import "MoreImageViewViewController.h"
                      
                      #define kUrl @"http://store.storeimages.cdn-apple.com/8748/as-images.apple.com/is/image/AppleInc/aos/published/images/s/38/s38ga/rdgd/s38ga-rdgd-sel-201601?wid=848&hei=848&fmt=jpeg&qlt=80&op_sharpen=0&resMode=bicub&op_usm=0.5,0.5,0,0&iccEmbed=0&layer=comp&.v=1454777389943"
                      
                      @interface MoreImageViewViewController ()
                      {
                          int imageIndex;
                      
                          NSMutableArray *threadArrays;
                      
                          UIImage *image;
                      }
                      
                      @end
                      
                      @implementation MoreImageViewViewController
                      
                      - (void)viewDidLoad {
                          [super viewDidLoad];
                      
                          UILabel *lable = [[UILabel alloc]initWithFrame:CGRectMake(100, 300, 0, 0)];
                          lable.text = @"点击屏幕停止加载";
                          lable.textColor = [UIColor blackColor];
                          [lable sizeToFit];
                          [self.view addSubview:lable];
                      
                          //创建多个UIImageView
                          self.title = @"多线程加载多张图片";
                          self.edgesForExtendedLayout = UIRectEdgeNone;
                          self.view.backgroundColor = [UIColor whiteColor];
                      
                          imageIndex = 100;
                      
                          for (int  row= 0; row<3; row++) {
                              for (int list = 0; list<2; list++) {
                      
                                  UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(10+list*200, 10+row*200, 200, 200)];
                      
                                  imageView.tag = imageIndex++;
                      
                                  [self.view addSubview:imageView];
                      
                              }
                          }
                      
                          threadArrays = [NSMutableArray array];
                      
                          //创建多个线程
                          for (int index = 0; index<6; index++) {
                              //此时我传的参数是线程创建的顺序
                              NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(downloadImage:) object:@(index)];
                      
                              //给线程设置优先级(0-1),优先级越高,被优先调用的几率越高。
                      //        thread.threadPriority = index/10.0;
                              thread.name = [NSString stringWithFormat:@"线程%d",index];
                              [thread start];
                      
                              [threadArrays addObject:thread];
                      
                          }
                      }
                      
                      //每条线程都会走这个方法,来下载相应的图片,在这里为了方便起见,我采用了同一个url图片
                      - (void)downloadImage:(NSNumber *)index{
                      
                          [NSThread sleepForTimeInterval:[index integerValue]];
                      
                          NSThread *currentThread = [NSThread currentThread];
                          //如果当前线程处于取消状态,则退出当前线程
                          if (currentThread.isCancelled) {
                              NSLog(@"thread(%@) will be cancelled!",currentThread);
                              [NSThread exit];//退出当前线程
                          }
                      
                          NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:kUrl]];
                      
                          image = [UIImage imageWithData:data];
                      
                          //用index找到相应线程的数据,赋给相应的图片试图。
                          [self performSelectorOnMainThread:@selector(updateUI:) withObject:index waitUntilDone:YES];
                      
                          NSThread *thread = [NSThread currentThread];
                          NSLog(@"当前线程是 = %@",thread.name);
                      
                      }
                      
                      - (void)updateUI:(NSNumber *)ktest{
                      
                              UIImageView *imageView = [self.view viewWithTag:100+[ktest integerValue]];
                      
                              imageView.image = image;
                      
                      }
                      
                      //点击屏幕将没有完成的线程设置为取消状态
                      - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
                      
                          for (int i=0; i<6; i++) {
                              NSThread *thread= threadArrays[i];
                              //判断线程是否完成,如果没有完成则设置为取消状态
                              //注意设置为取消状态仅仅是改变了线程状态而言,并不能终止线程
                              if (!thread.isFinished) {
                                  [thread cancel];
                      
                                  NSLog(@"============");
                      
                              }
                          }
                      
                      }
                      
                      @end

                      顺序启动的线程一般不会按照启动顺序执行,这是因为线程都创建好以后,CPU会根据实际情况(网速、启动时间、优先级等)来决定执行线程的先后顺序,在这里我采用线程sleep的方式实现了线程的顺序执行。
                    7. 文/涅槃广广(简书作者)
                      原文链接:http://www.jianshu.com/p/b1c2bd572e81
                      著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

                      补充

                      为了简化多线程开发过程,苹果官方对NSObject进行分类扩展(本质还是创建NSThread),对于简单的多线程操作可以直接使用这些扩展方法。

                      //在后台执行一个操作,本质就是重新创建一个线程执行当前方法。
                      - (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg:
                      
                      //在指定的线程上执行一个方法,需要用户创建一个线程对象。
                      - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait:
                      
                      //在主线程上执行一个方法(前面已经使用过)。
                      - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait:
  • 相关阅读:
    Option使用和实现内幕源码揭秘之Scala学习笔记-22
    模式匹配高级实战:嵌套的Case class之Scala学习笔记-21
    Case class和Case object代码实战解析之Scala学习笔记-20
    Scala提取器Extractor实战详解之Scala学习笔记-19
    Type、Array、List、Tuple模式匹配实战解析之Scala学习笔记-18
    Scala中模式匹配入门实战详解之Scala学习笔记-17
    FragmentTabHost
    android项目中使用开源数据库litepal
    android Studio项目运行时报错“Could not identify launch activity: Default Activity not found”
    DrawerLayout学习,抽屉效果
  • 原文地址:https://www.cnblogs.com/ios-wanglong/p/5235943.html
Copyright © 2011-2022 走看看