iOS:多线程(二) —— NSThread
程序员文章站
2024-03-24 13:12:58
...
前言
在日常开发中,多线程的使用能帮助我们解决很多问题,比如大量数据的运算,复杂程序的执行,以及利用锁来实现一些需求,本系列文章主要介绍 iOS 中多线程实现技术的用法。
关于 pthread 的介绍和使用请查看之前的文章,本篇文章针对 NSThread 来赘述。
关于 NSThread
NSThread 是苹果官方提供给我们的一种面向对象的轻量级多线程解决方案,一个 NSThread 对象代表一个线程,需要程序员手动管理线程的生命周期,处理线程同步等问题。
NSThread 使用
常用方法
在日常开发中,我们经常会用 [NSThread currentThread]
来获取当前线程,便于开发调试,这是最常用的一个方法,除此之外,下面的这几个方法,使用频率也是非常高,基于 NSObject
的 NSThreadPerformAdditions
分类中的方法,继承自 NSObject
的子类都可以很方便的调用。
// 当前线程睡到指定时间
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
// 线程沉睡时间间隔 常用在设置启动页间隔
[NSThread sleepForTimeInterval:1.0];
// 返回调用堆栈信息 可用于调试
// [NSThread callStackSymbols] return NSArray
// [NSThread callStackReturnAddresses] return NSArray
NSLog(@"callStackSymbols : %@", [NSThread callStackSymbols]);
NSLog(@"callStackReturnAddresses : %@", [NSThread callStackReturnAddresses]);
@interface NSObject (NSThreadPerformAdditions)
// 指定方法在主线程中执行
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
// equivalent to the first method with kCFRunLoopCommonModes
// 指定方法在某个线程中执行
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)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));
// equivalent to the first method with kCFRunLoopCommonModes
// 指定方法在开启的子线程中执行
- (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
@end
创建线程
-
实例方法创建线程
实例方法创建线程,可以根据需要设置参数,调用- start()
才能开启线程,调用- cancel()
取消线程
// SEL
NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(nsThreadMethod:) object:@"thread"];
NSThread *thread2 = [[NSThread alloc] init];
// block 方式
NSThread *thread3 = [[NSThread alloc] initWithBlock:^{
NSLog(@"block 创建 thread %s : %@", __func__, [NSThread currentThread]);
}];
// 设置名称
thread1.name = @"thread1";
// 设置线程优先级 调度优先级的取值范围是0.0 ~ 1.0,默认0.5,值越大,优先级越高。
thread3.threadPriority = 0.0;
// 启动线程
[thread1 start];
// 线程是否正在执行
if ([thread3 isExecuting]) {
NSLog(@"thread1 is executing! ");
}
// 取消线程
[thread1 cancel];
// 线程是否撤销
if ([thread1 isCancelled]) {
NSLog(@"thread1 canceled!");
}
// 线程是否执行结束
if ([thread3 isFinished]) {
NSLog(@"thread3 is finished!");
}
-
类方法创建线程
类方法创建NSThread
不需要再调用start
方法,设置参数是通过类方法设置
// block 方式
[NSThread detachNewThreadWithBlock:^{
NSLog(@"类方法 block 创建 thread : %s : %@", __func__, [NSThread currentThread]);
}];
// SEL 方式
[NSThread detachNewThreadSelector:@selector(nsThreadMethod:) toTarget:self withObject:nil];
/*
[NSThread currentThread]; 获取当前线程
[NSThread isMultiThreaded]; 当前代码运行线程是否为子线程
*/
// 当前线程睡到指定时间
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
// 线程沉睡时间间隔 常用在设置启动页间隔
[NSThread sleepForTimeInterval:1.0];
// 获取线程优先级 / 设置优先级
double priority = [NSThread threadPriority];
NSLog(@"当前线程优先级 : %f", priority);
[NSThread setThreadPriority:0.9];
线程间通信
通常,例如我们有个网络请求是在子线程中执行,请求成功后我么要回到主线程中刷新UI,这是时候我们就需要了解子线程和主线程之间的通信,NSThread
为我们提供了解决方案,调用 NSObject
和 NSObject (NSThreadPerformAdditions)
分类中的方法,所有继承自 NSObject
实例化对象都可调用以下方法
// 指定方法在主线程中执行
[self performSelectorOnMainThread:@selector(performMethod:) // 要执行的方法
withObject:nil // 执行方法时,要传入的参数 类型为 id
waitUntilDone:YES]; // 当前线程是否要被阻塞,直到主线程将我们指定的代码块执行完,当前线程为主线程,设置为YES时,会立即执行,为NO时加入到RunLoop中在下一次运行循环时执行
[self performSelectorOnMainThread:@selector(performMethod:)
withObject:nil
waitUntilDone:YES
modes:@[@"kCFRunLoopDefaultMode"]];
// 指定方法在某个线程中执行
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)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;
// 在当前线程上执行操作,调用 NSObject 的 performSelector:相关方法
- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
// 指定方法在开启的子线程中执行 (相当于创建了一个子线程,并且执行方法)
[self performSelectorInBackground:@selector(performMethod:) withObject:nil];
举个例子,我们来模拟网络请求成功回到线程刷新 UI 的实现
// 开辟子线程模拟网络请求
[NSThread detachNewThreadWithBlock:^{
NSLog(@"类方法 block 创建 thread : %s : %@", __func__, [NSThread currentThread]);
// 模拟网络请求耗时操作
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"网络请求中 %@",[NSThread currentThread]);
}
NSLog(@"网络请求成功 准备回到主线程刷新 UI %@",[NSThread currentThread]);
// 主线程刷新UI
[self performSelectorOnMainThread:@selector(mainThreadRefreshUI) withObject:nil waitUntilDone:YES];
}];
// 主线程刷新 UI 调用方法
- (void)mainThreadRefreshUI {
NSLog(@"回到了主线程并且刷新 UI %s : %@", __func__, [NSThread currentThread]);
}
跟我们预想的一样,网络请求耗时操作是在子线程中执行,执行结束后调用线程间通信方法回到了主线程刷新 UI。
以上是关于 NSThread
的介绍和简单使用的说明,相关 demo 请参考
https://github.com/G-Jayson/Multi-thread