iOS 多线程之 NSThread

2,684 阅读6分钟

iOS 多线程之 基础概念

iOS 多线程之 NSThread

iOS 多线程之 NSOperation

iOS 多线程之 GCD

NSThread 简介

NSThread 是苹果官方提供的,使用起来比 pthread 更加面向对象,简单易用,可以直接操作线程对象。不过也需要需要程序员自己管理线程的生命周期(主要是创建),在开发的过程中偶尔使用 NSThread。比如经常调用[NSThread currentThread]来显示当前的进程信息或调用[NSThread isMainThread]来判断当前的进程是否为主线程。

NSThread 使用

创建线程

/// 返回NSThread使用给定参数初始化的对象。
/// target 		目标对象。
/// selector	方法选择器 该选择器最多使用一个参数,并且不能具有返回值。
/// object		单个参数传递给目标。可以传 nil。
NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
/// 线程名称
thread1.name = @"thread1";
///启动线程
[thread1 start];

- (void)run {
    for (int i = 0; i < 100; i++) {
        NSLog(@"%i-%@",i,NSThread.currentThread.name);
    }
}


NSThread *thread2 = [[NSThread alloc] initWithBlock:^{
  
    for (int i = 0; i < 100; i++) {
        NSLog(@"%i-%@",i,NSThread.currentThread.name);
    }
}];
thread2.name = @"thread1";
[thread2 start];


ZYThread *thread3 = [[ZYThread alloc] init];
thread3.name = @"thread3";
[thread3 start];

启动线程

  • - (void)start;

启动线程(内部调用main方法)。

此方法异步生成新线程,并在新线程上调用接收方的main方法。一旦线程开始执行,executing属性返回YES,这可能发生在start方法返回之后。如果使用目标和选择器初始化接收器,则默认的main方法会自动调用该选择器。如果此线程是应用程序中分离的第一个线程,则此方法将带有对象nilNSWillBecomeMultiThreadedNotification发送到默认通知中心。

/// 启动线程(内部调用main方法)
[thread1 start];
  • - (void)main;

线程的入口方法。此方法的默认实现是调用指定目标上的选择器。如果自定义NShread子类,则可以重写此方法使用它来实现线程的任务。不需要调用super完成默认的实现。不直接调用此方法。始终通过调用start方法来启动线程。

@implementation ZYThread

- (void)main {
  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSLog(@"%@", ZYThread.currentThread.name);
}
@end

ZYThread *thread1 = [[ZYThread alloc] init];
//启动线程
[thread1 start];
  • (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument;

分离新线程并开启,使用指定的选择器作为线程的任务。无法拿到返回对象,因此无法进行更加详细的设置

//分离新线程并开启,使用指定的选择器作为线程的任务。
/// selector	方法选择器 该选择器最多使用一个参数,并且不能具有返回值。
/// target 		方法调用者对象。
/// object		单个参数传递给目标。可以传 nil。
[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];

[NSThread detachNewThreadWithBlock:^{
    for (int i = 0; i < 100; i++) {
        NSLog(@"%i-%@",i,NSThread.currentThread.name);
    }
}];

停止/阻塞线程

  • + (void)sleepUntilDate:(NSDate *)date;

阻止当前线程直到指定的时间。当线程被阻塞时,不会发生运行循环处理。

  • + (void)sleepForTimeInterval:(NSTimeInterval)ti;

指定的时间间隔内阻止当前线程直到。当线程被阻塞时,不会发生运行循环处理。

  • + (void)exit;

终止当前线程。

  • - (void)cancel;

停止执行当前任务。将线程设置为取消状态。

获取线程执行状态

  • @property(readonly, getter=isExecuting) BOOL executing;

一个布尔值,判断线程是否正在执行。如果线程正在执行,返回 YES,否则返回 NO。

  • @property(readonly, getter=isFinished) BOOL finished;

一个布尔值,判断线程是否已完成执行。如果线程已完成执行,返回 YES,否则返回 NO。

  • @property(readonly, getter=isCancelled) BOOL cancelled;

一个布尔值,判断线程是否被取消执行(可以取消)。如果线程被取消执行,返回 YES,否则返回 NO。调用对象的cancel方法可将此属性的值设置为 YES。如果线程支持取消,应该定期(在main方法)检查该属性,如果返回 YES 就退出。

// 重写内部的main方法 官方建议:在自定义任务每执行完一个耗时操作就判断当前任务是否被取消,如果被取消就直接返回
- (void)main {
    for (int i = 0; i < 10000; i++) {
        NSLog(@"1--%i--%@",i,[NSThread currentThread]);
    }
    
  
    if (self.isCancelled) return;//检查 cancelled 属性, 如果返回 YES 就退出

    for (int i = 10000; i < 20000; i++) {
        NSLog(@"2--%i--%@",i,[NSThread currentThread]);
    }
    
    if (self.isCancelled) return; //检查 cancelled 属性, 如果返回 YES 就退出
    
    for (int i = 20000; i < 30000; i++) {
        NSLog(@"3--%i--%@",i,[NSThread currentThread]);
    }
}

主线程相关

  • @property(class, readonly) BOOL isMainThread;

一个布尔值,判断当前线程是否是主线程。如果是主线程,返回 YES,否则返回 NO。

  • @property(readonly) BOOL isMainThread;

一个布尔值,判断指定线程是否是主线程。如果是主线程,返回 YES,否则返回 NO。

  • @property(class, readonly, strong) NSThread *mainThread;

获取主线程对象。

查询线程环境

  • + (BOOL)isMultiThreaded;

返回应用程序是否是多线程的。

  • @property(class, readonly, strong) NSThread *currentThread;

获取当前执行的线程对象。

  • @property(class, readonly, copy) NSArray<NSNumber *> *callStackReturnAddresses;

获取调用堆栈返回地址的数组。数组元素都是一个包装 NSUInteger 值的 NSNumber 对象。

  • @property(class, readonly, copy) NSArray<NSString *> *callStackSymbols;

获取调用堆栈符号的数组。

线程信息属性

  • @property(copy) NSString *name;

线程的名称。

  • @property NSUInteger stackSize;

线程的堆栈大小,以字节为单位。默认是512K。

线程优先级

  • @property double threadPriority;

线程的优先级,由0.0到1.0之间的浮点数指定,其中1.0是最高优先级。默认是 0.5。优先级更高的线程,被CPU调度到的概率会更高。

  • + (BOOL)setThreadPriority:(double)p;

设置当前线程的优先级。如果优先级设置成功 返回 YES,否则 返回 NO 。

  • @property NSQualityOfService qualityOfService;

NSQualityOfService:

NSQualityOfServiceUserInteractive:最高优先级,主要用于提供交互UI的操作,比如处理点击事件,绘制图像到屏幕上

NSQualityOfServiceUserInitiated:次高优先级,主要用于执行需要立即返回的任务

NSQualityOfServiceDefault:默认优先级,当没有设置优先级的时候,线程默认优先级

NSQualityOfServiceUtility:普通优先级,主要用于不需要立即返回的任务

NSQualityOfServiceBackground:后台优先级,用于完全不紧急的任务

NSThread 线程间通讯

  • 线程间通信: 在1个进程中,线程往往不是孤立存在的,多个线程之间需要经常进行通信。

  • **线程间通信的体现:**1个线程传递数据给另1个线程; 在1个线程中执行完特定任务后,转到另1个线程继续执行任务。

在当前线程上执行操作

调用NSObjectperformSelector:相关方法。

/// 将指定的消息发送到接收方并返回消息的结果。
/// aSelector 指定的消息,消息不应包含参数。
- (id)performSelector:(SEL)aSelector;

/// 将指定的消息发送到接收方并返回消息的结果。
/// aSelector 指定的消息
/// object 作为消息唯一参数(对象)。
- (id)performSelector:(SEL)aSelector withObject:(id)object;

/// 将指定的消息发送到接收方并返回消息的结果。
/// aSelector 指定的消息
/// object1 作为消息的第一个参数(对象)
/// object2 作为消息的第二个参数(对象)
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;

以下方法位于NSObject (NSThreadPerformAdditions)分类中,所有继承NSObject实例化对象都可调用以下方法

在主线程上执行操作

苹果声明 UI 更新一定要在 UI 线程(主线程)中执行,虽然不是所有后台线程更新 UI 都会出错。

/// 使用默认模式在主线程上调用接收方的方法。
/// aSelector 要调用的方法的选择器。该方法没有返回值,并且应采用 id 类型的单个参数,或者不带参数。
/// arg       方法的参数。如果方法没有参数,则传递 nil。
/// wait			(完成操作之前)线程是否阻塞。指定 YES 阻止线程(后面的操作等待至该操作完成在执行),否则,指定 NO。
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;

/// 使用指定的模式在主线程上调用接收器的方法。
/// aSelector 要调用的方法的选择器。该方法没有返回值,并且应采用 id 类型的单个参数,或者不带参数。
/// arg       方法的参数。如果方法没有参数,则传递 nil。
/// wait			(完成操作之前)线程是否阻塞。指定 YES 阻止线程(后面的操作等待至该操作完成在执行),否则,指定 NO。
/// array			允许执行操作的模式的数组。该数组必须至少包含一模式。如果nil为此参数指定或为空数组,则此方法将不执行操作。
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray<NSString *> *)array;

在指定线程上执行操作

/// 使用默认模式在指定线程上调用接收器的方法。
/// aSelector 要调用的方法的选择器。该方法没有返回值,并且应采用 id 类型的单个参数,或者不带参数。
/// thr				指定的线程
/// arg       方法的参数。如果方法没有参数,则传递 nil。
/// wait			(完成操作之前)线程是否阻塞。指定 YES 阻止线程(后面的操作等待该操作完成再执行),否则,指定 NO。
/// array			允许执行操作的模式的数组。该数组必须至少包含一模式。如果nil为此参数指定或为空数组,则此方法将不执行操作。
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;

/// 使用指定的模式在指定的线程上调用接收器的方法。
/// aSelector 要调用的方法的选择器。该方法没有返回值,并且应采用 id 类型的单个参数,或者不带参数。
/// thr				指定的线程
/// arg       方法的参数。如果方法没有参数,则传递 nil。
/// wait			(完成操作之前)线程是否阻塞。指定 YES 阻止线程(后面的操作等待至该操作完成在执行),否则,指定 NO。
/// array			允许执行操作的模式的数组。该数组必须至少包含一模式。如果nil为此参数指定或为空数组,则此方法将不执行操作。
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array;

在新后台线程上执行操作

/// 在新线程上执行操作 此方法在您的应用程序中创建一个新线程,如果尚未将其置于多线程模式,则将其置于多线程模式。
/// aSelector 要调用的方法的选择器。该方法没有返回值,并且应采用 id 类型的单个参数,或者不带参数。
/// arg       方法的参数。如果方法没有参数,则传递 nil。
- (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg 

iOS 多线程之 基础概念

iOS 多线程之 NSThread

iOS 多线程之 NSOperation

iOS 多线程之 GCD