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方法会自动调用该选择器。如果此线程是应用程序中分离的第一个线程,则此方法将带有对象nil的NSWillBecomeMultiThreadedNotification发送到默认通知中心。
/// 启动线程(内部调用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个线程继续执行任务。
在当前线程上执行操作
调用NSObject的performSelector:相关方法。
/// 将指定的消息发送到接收方并返回消息的结果。
/// 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