今天看啥
    热点:

      天发国际娱乐官网:"一带一路"建设是开放的、包容的,欢迎世界各国和国际、地区组织积极参与。

      多线程-NSThread介绍,多线程-nsthread


      NSThread简介

      NSThread封装性差,最偏向于底层,主要基于thread使用,OC语言,需要程序员自己管理生命周期.
      每一个NSThread对象代表着一个线程,理解NSThread更有利于理解多线程的含义.

      NSThread API

      线程创建

      有返回值初始化方法有3种:

      - (instancetype)init API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0)) NS_DESIGNATED_INITIALIZER;
      - (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
      - (instancetype)initWithBlock:(void (^)(void))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));

      init方法初始化线程并返回,线程的入口函数由Selector传入。线程创建出来之后需要手动调用-start方法启动
      无返回值(隐式创建)初始化方法有2种:

      + (void)detachNewThreadWithBlock:(void (^)(void))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
      + (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument;

      分离新的子线程,直接创建并启动一个线程去执行Selector中的方法,由于没有返回值,如果需要获取新创建的Thread,需要在执行的Selector中的方法中调用[NSThread currentThread]获取

      线程操作

      NSThread给线程提供的主要操作方法有启动,睡眠,取消,退出.

      线程启动

      我们使用init方法将线程创建出来之后,线程并不会立即运行,只有我们手动调用-start方法才会启动线程

      - (void)start API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));

      注意:部分线程属性需要在启动前设置,线程启动之后再设置会无效。如qualityOfService(线程优先级)属性

      线程睡眠

      + (void)sleepUntilDate:(NSDate *)date;//线程休眠(阻塞),休眠到指定日期时间
      + (void)sleepForTimeInterval:(NSTimeInterval)ti;//线程休眠(阻塞),休眠到指定日期时长

      看到sleepUntilDate:大家可能会想起runloop的runUntilDate:。他们都有阻塞线程的效果,但是阻塞之后的行为又有不一样的地方,使用的时候,我们需要根据具体需求选择合适的API。

      sleepUntilDate:相当于执行一个sleep的任务。在执行过程中,即使有其他任务传入runloop,runloop也不会立即响应,必须sleep任务完成之后,才会响应其他任务 runUntilDate:虽然会阻塞线程,阻塞过程中并不妨碍新任务的执行。当有新任务的时候,会先执行接收到的新任务,新任务执行完之后,如果时间到了,再继续执行runUntilDate:之后的代码

      例子:

      -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
          self.view.backgroundColor = [UIColor yellowColor];
          [self createWithDetach];
      }
      
      - (void)createWithDetach{
           //隐式创建
          [NSThread detachNewThreadSelector:@selector(delay:) toTarget:self withObject:@"date"];
          __weak typeof(self)weakSlef = self;
          [NSThread detachNewThreadWithBlock:^{
              NSLog(@"ssss");
              [weakSlef delay:nil];
          }];
      }
      - (void)delay:(id)parm{
          if ([@"date" isEqual:parm]) {
              [NSThread sleepForTimeInterval:10];//该方法后面的代码,10秒后执行
              NSLog(@"暂停10秒执行");
          }else{
              [NSThread sleepForTimeInterval:2];
              NSLog(@"暂停2秒执行");
          }
      }

      阻塞<喎?https://www.1click-soft.com/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxoNCBpZD0="线程取消">线程取消

      - (void)cancel API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
      @property (readonly, getter=isCancelled) BOOL cancelled API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
      不过大家千万不要被它的名字迷惑,调用cancel方法并不会立刻取消线程,它仅仅是将cancelled属性设置为YES。cancelled也仅仅是一个用于记录状态的属性。线程取消的功能需要我们在main函数中自己实现 要实现取消.的功能,我们需要自己在线程的main函数中定期检查isCancelled状态来判断线程是否需要退出,当isCancelled为YES的时候,我们手动退出。如果我们没有在main函数中检查isCancelled状态,那么调用cancel将没有任何意义.

      错误用法示例:

      -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
          self.view.backgroundColor = [UIColor yellowColor];
          [self cancelThread];
      }
      
      -(void)cancelThread{
          NSThread * t1 = [[NSThread alloc]initWithTarget:self selector:@selector(threadCancel:) object:@"取消线程"];
          t1.name = @"取消研究";
          [t1 start];
      }
      - (void)threadCancel:(id)parm{
          NSLog(@"取消前");
          [[NSThread currentThread] cancel];
          NSLog(@"取消后");
          if ([[NSThread currentThread] isCancelled])
              NSLog(@"取消了");
      }

      错误用法
      可以看到并没有取消,虽然isCancelled判断为YES,但是仍然往后执行了.

      线程退出

      + (void)exit;
      exit调用之后会立即终止线程,即使任务还没有执行完成也会中断。这就非常有可能导致内存泄露等严重问题,所以一般不推荐使用。 对于有runloop的线程,可以使用CFRunLoopStop()结束runloop配合-cancel结束线程

      线程状态

      executing是否正在执行,finished是否已经结束,cancelled是否已经取消了

      @property (readonly, getter=isExecuting) BOOL executing API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));//线程是否正在执行
      @property (readonly, getter=isFinished) BOOL finished API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));//线程是否已经结束
      @property (readonly, getter=isCancelled) BOOL cancelled API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));//线程是否已经取消

      主线程,当前线程,多线程判断或获取

      isMainThread是否是主线程,mainThread获取主线程,class修饰的属性表示是一个类属性([NSThread isMainThread])
      @property (readonly) BOOL isMainThread API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
      @property (class, readonly) BOOL isMainThread API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0)); // reports whether current thread is main
      @property (class, readonly, strong) NSThread *mainThread API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
      currentThread获取当前线程
      @property (class, readonly, strong) NSThread *currentThread;
      isMultiThreaded 是否是多线程
      + (BOOL)isMultiThreaded;

      线程设置(名称,优先级)

      name线程名称,主要用于debug快速定位对应出哪个线程出现的问题

      @property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
      threadPriority设置和获取线程优先级,范围0.0-1.0优先级越高越先执行,默认是0.5,iOS8之后新增了qualityOfService枚举属性,大家可以通过枚举值设置优先级 线程在创建后会放在线程调度池里,当开启线程时会根据设置的优先级去优先调哪个线程,所以设置优先级一点要在调start方法开启之前设置.
      + (double)threadPriority;
      + (BOOL)setThreadPriority:(double)p;
      
      @property double threadPriority API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0)); // To be deprecated; use qualityOfService below
      
      @property NSQualityOfService qualityOfService API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0)); // read-only after the thread is started

      优先级NSQualityOfService主要有5个枚举值,优先级别从高到低排布:

      typedef NS_ENUM(NSInteger, NSQualityOfService) {
          NSQualityOfServiceUserInteractive = 0x21,
          NSQualityOfServiceUserInitiated = 0x19,
          NSQualityOfServiceDefault = -1,
          NSQualityOfServiceUtility = 0x11,
          NSQualityOfServiceBackground = 0x09
      } API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0));
      NSQualityOfServiceUserInteractive:最高优先级,主要用于提供交互UI的操作,比如处理点击事件,绘制图像到屏幕上 NSQualityOfServiceUserInitiated:次高优先级,主要用于执行需要立即返回的任务 NSQualityOfServiceDefault:默认优先级,当没有设置优先级的时候,线程默认优先级 NSQualityOfServiceUtility:普通优先级,主要用于不需要立即返回的任务 NSQualityOfServiceBackground:后台优先级,用于完全不紧急的任务

      例子:

      //线程优先级
      - (void)threadPriority{
          NSThread * t1 = [[NSThread alloc]initWithBlock:^{
              NSLog(@"%@",[NSThread currentThread]);
          }];
          t1.threadPriority = 0.3;
          t1.name = @"t1";
          NSThread * t2 = [[NSThread alloc]initWithBlock:^{
              NSLog(@"%@",[NSThread currentThread]);
          }];
          t2.threadPriority = 0.6;
          t2.name = @"t2";
          NSThread * t3 = [[NSThread alloc]initWithBlock:^{
              NSLog(@"%@",[NSThread currentThread]);
          }];
          t3.threadPriority = 0.5;
          t3.name = @"t3";
      
          NSThread * t4 = [[NSThread alloc]initWithBlock:^{
              NSLog(@"%@",[NSThread currentThread]);
          }];
          t4.qualityOfService = NSQualityOfServiceUserInteractive;
          t4.name = @"t4";
      
          [t1 start];
          [t2 start];
          [t3 start];
          [t4 start];
      }

      线程通知

      NSThread有三个线程相关的通知,

      FOUNDATION_EXPORT NSNotificationName const NSWillBecomeMultiThreadedNotification;
      FOUNDATION_EXPORT NSNotificationName const NSDidBecomeSingleThreadedNotification;
      FOUNDATION_EXPORT NSNotificationName const NSThreadWillExitNotification;

      NSWillBecomeMultiThreadedNotification:由当前线程派生出第一个其他线程时发送,一般一个线程只发送一次
      NSDidBecomeSingleThreadedNotification:这个通知目前没有实际意义,可以忽略
      NSThreadWillExitNotification线程退出之前发送这个通知

      线程通讯

      完成一些操作需要多线程会更加的有效,不同的线程做不同任务,互相有一些依赖,这就需要线程之间进行通讯.NSThread头文件中有一个NSObject的NSThreadPerformAdditions分类,只要继承NSObject的类都可以用里面的方法进行通讯,共5个方法:

      #import "JThread.h"//自定义继承NSThread的线程
      
      @implementation JThread
      //结束线程的时候,我们可以使用CFRunLoopStop()配合-cancel来结束线程
      -(void)cancelThread{
          [[NSThread currentThread] cancel];
          CFRunLoopStop(CFRunLoopGetCurrent());
      }
      @end
      
      @interface NSObject (NSThreadPerformAdditions)
      
      - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray *)array;
      
      - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
      
      - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray *)array API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
      
      - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
      
      - (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
      
      @end
      performSelectorOnMainThread:将aSelector里的方法放到主线程中执行; performSelector:(SEL)aSelector onThread::将aSelector里的方法放到指定线程中执行; performSelectorInBackground:开启一个后台子线程,并执行aSelector里的方法. wait参数表示是否阻塞,YES是阻塞的,等线程中的方法执行完才会去执行对应线程中的方法,NO就是不阻塞,直接执行对应线程中的方法;如果本身是主线程有调用了performSelectorOnMainThread方法,wait参数是无效的,不论是设置为YES还是NO都会立即执行.

      实例应用

      @interface ViewController ()
      @property(nonatomic ,copy) JThread * customThread;
      @end
      @implementation ViewController
      -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
          self.view.backgroundColor = [UIColor yellowColor];
          [self threadExample];
      }
      
      - (void)threadExample{
          [self.customThread start];//启动线程
          [self performSelector:@selector(handleTimeConsumingOperation) onThread:self.customThread withObject:nil waitUntilDone:NO];//线程间通讯
      }
      -(NSThread *)customThread
      {
          if (!_customThread) {
              _customThread = [[JThread alloc]initWithTarget:self selector:@selector(threadTest:) object:@"创建一个新的线程"];//创建
              _customThread.name = @"customThread";//设置名称
              _customThread.qualityOfService = NSQualityOfServiceDefault;//设置优先级
          }
          return _customThread;
      }
      - (void)threadTest:(id)pram{
          NSRunLoop * runLoop = [NSRunLoop currentRunLoop];//给当前线程添加runLoop
          [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];//给runLoop添加数据源
          while (![[NSThread currentThread] isCancelled]) {
              [runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:10]];//启动runLoop;
          }
      }
      - (void)handleTimeConsumingOperation{
      
          for (int i = 0; i < 10000; i++) {
              NSLog(@"%@:%zd",[NSThread currentThread],i);
              if (i == 500) {
                  [self.customThread cancelThread];
                  NSLog(@"在当前线程中的方法取消线程,当前方法任然继续执行,但后面再添加方法到取消的线程中执行,则不会执行了");
                  [self performSelector:@selector(test) onThread:self.customThread withObject:nil waitUntilDone:NO];//线程间通讯
                  self.customThread = nil;
                  break;
              }
          }
      }
      - (void)test{
          NSLog(@"不会再执行了");
      }
      @end

      www.1click-soft.comtruehttp://www.1click-soft.com/qtjc/1304354.htmlTechArticle多线程-NSThread介绍,多线程-nsthread NSThread简介 NSThread封装性差,最偏向于底层,主要基于thread使用,OC语言,需要程序员自己管理生命周期. 每...

      相关文章

      帮客评论

      视觉看点
      百度 360 搜狗