IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> iOS 多线程基础之 NSThread -> 正文阅读

[移动开发]iOS 多线程基础之 NSThread

前言

通常在 iOS 中,有三种比较常见的方式实现多线程,分别是 NSThreadGCDNSOperation。本文主要介绍它们当中使用频率较低的 NSThread

NSThread

NSThread 是 Foundation 框架中封装的线程类,其对象就表示程序的一个线程。

1. 线程创建

我们有下面三种方法可以创建新线程去执行任务。

方法一:常规方法创建新线程

这种方法比较常规,通过对象创建和初始化方法进行新线程的创建。虽然写代码的步骤看起来比较复杂,但是可以获取到线程的对象,可以对线程进行名称、优先级之类的个性化修改。

NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadRun) object:nil];
    
NSThread *thread = [[NSThread alloc] initWithBlock:^{
	NSLog(@"Run %@", [NSThread currentThread]);
}];

// 需要手动调用 start 方法
[thread start];

threadRun 方法表示线程将要执行的任务

- (void)threadRun {
    NSLog(@"Run %@", [NSThread currentThread]);
}

方法二:detachNewThread 系列类方法

我们可以通过 detachNewThread 系列的类方法创建新线程来执行任务,代码会比较简洁。但是这些方法都是没有返回值的,这就意味着我们无法获取到新创建的线程,进行无法修改线程设置。

[NSThread detachNewThreadSelector:@selector(threadRun) toTarget:self withObject:nil];

[NSThread detachNewThreadWithBlock:^{
    NSLog(@"Run %@", [NSThread currentThread]);
}];

方法三:NSObject + NSThreadPerformAdditions 分类中提供的方法

NSObject + NSThreadPerformAdditions 分类中提供的 performSelectorInBackground: withObject: 方法也可以创建新线程执行任务,它的使用情况和方法二相似。

[self performSelectorInBackground:@selector(threadRun) withObject:nil];

2. 线程优先级

线程是具有优先级的,线程的优先级代表 CPU 调度的优先级,优先级高的线程更容易得到 CPU 资源执行任务。对于 NSThread 对象,我们可以通过 threadPriority 属性来修改它的优先级,优先级的取值范围为 [ 0.0 , 1.0 ] [0.0, 1.0] [0.0,1.0],默认优先级为 0.5。

优先级实验

我们创建三个线程,优先级分别为 1.0 / 0.1 / 默认,让它们执行同一个任务,根据控制台输出结果,查看各优先级线程的任务执行情况。

// 设置线程 A 的优先级为 1.0
NSThread *threadA = [[NSThread alloc] initWithTarget:self selector:@selector(threadRun) object:nil];
threadA.name = @"threadA";
threadA.threadPriority = 1.0;
[threadA start];

// 设置线程 B 的优先级为 0.1
NSThread *threadB = [[NSThread alloc] initWithTarget:self selector:@selector(threadRun) object:nil];
threadB.name = @"threadB";
threadB.threadPriority = 0.1;
[threadB start];

// 让线程 C 保持默认优先级(0.5)
NSThread *threadC = [[NSThread alloc] initWithTarget:self selector:@selector(threadRun) object:nil];
threadC.name = @"threadC";
[threadC start];

// 线程要执行的任务
- (void)threadRun {
    for (int i = 0; i < 100; i++) {
        NSLog(@"Run %@ - %d", [NSThread currentThread].name, i);
    }
}

根据上面例子的执行结果可以看出来,优先级高的线程能更快地完成它需要执行的任务

新的优先级属性

从官方的注释中,我们发现 threadPriority 属性在将来会被弃用,用 qualityOfService 作为新的线程优先级。

// 新版的线程优先级
@property NSQualityOfService qualityOfService;

typedef NS_ENUM(NSInteger, NSQualityOfService) {
	// 和图形处理相关的任务,比如滚动和动画
    NSQualityOfServiceUserInteractive = 0x21,
    // 用户请求的任务,但是不需要精确到毫秒级。例如,用户请求打开电子邮件App来查看邮件
    NSQualityOfServiceUserInitiated = 0x19,
    // 周期性的用户请求任务。比如,电子邮件App可能被设置成每5分钟自动检测新邮件
    NSQualityOfServiceUtility = 0x11,
    // 后台任务,对这些任务用户可能并不会察觉
    NSQualityOfServiceBackground = 0x09,
    // 默认的优先级,优先级在 Utility 之前
    NSQualityOfServiceDefault = -1
} API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0));

3. 线程间通信

通常线程间通信主要有下面两种情形:

  • 一个线程的数据传递给另一个线程
  • 一个线程的任务执行完后,转到另一个线程继续执行任务

与 NSThread 有关的线程间通信实现的方法在 NSObject + NSThreadPerformAdditions 分类中,常用的是下面两个方法。

// 到主线程执行指定任务
- (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

线程间通信实例

这里用一个经典实例,来对线程间通信进行讲解。我们要在网上下载图片,然后展示在手机屏幕上显示。为了不阻塞主线程,我们需要异步进行图片的下载,又因为 iOS 不能在子线程更新 UI,我们需要在图片下载完成之后回到主线程将下载好的图片更新到 UIImageView 上

@interface ViewController ()

// 用于展示图片
@property (nonatomic, strong) UIImageView *imageView;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self.view addSubview:self.imageView];
  	// 设置约束
    [self.imageView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.center.equalTo(self.view);
        make.size.mas_equalTo(CGSizeMake(216, 384));
    }];
    // 创建线程
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(downloadImage) object:nil];
    // 启动线程
    [thread start];
}

#pragma mark - Actions

// 后台线程处理方法
- (void)downloadImage {
	// 耗时操作,下载网络图片
    NSURL *url = [NSURL URLWithString:@"https://pics3.baidu.com/feed/b151f8198618367afc010b4f3e2a1dd2b21ce573.jpeg?token=a80568b00de1ae2814069f2f70241a96"];
    NSData *imgData = [NSData dataWithContentsOfURL:url];
    
    // 执行耗时操作完成,返回主线程处理
    [self performSelectorOnMainThread:@selector(updateUIWithData:) withObject:imgData waitUntilDone:YES];
}

// 需要在主线程执行的操作
- (void)updateUIWithData:(NSData *)data {
	// 刷新 UI 的方法
    UIImage *image = [UIImage imageWithData:data];
    self.imageView.image = image;
}

#pragma mark - Getter

- (UIImageView *)imageView {
    if (!_imageView) {
        _imageView = [[UIImageView alloc] init];
        _imageView.backgroundColor = [UIColor grayColor];
    }
    return _imageView;
}

@end

4. 常用属性和方法

因为前面上面提到的 NSThread 使用方式都有更加便捷的 GCD 函数代替,所以相比之下,下面这些方法反而会更加常用。

改变线程状态的方法

// 启动线程执行任务,创建 -> 就绪
[thread start];
// 标记线程为 cancelled 状态
[thread cancel];
// 退出线程执行, -> 终止
[NSThread exit];

判断线程状态的方法

// 判断任务是否被取消
BOOL isCancelled = [thread isCancelled];
// 判断任务是否正在执行
BOOL isExecuting = [thread isExecuting];
// 判断任务是否执行完成
BOOL isFinished = [thread isFinished];

主线程判断的方法

// 判断 thread 是否为主线程
BOOL isMainThread = [thread isMainThread];

执行线程任务的方法

// 在当前线程中执行 thread 对象的任务
[thread main];

NSThread 常用类方法

// 判断是否处在多线程环境
[NSThread isMultiThreaded];

// 获取当前线程
[NSThread currentThread]

// 获取主线程
[NSThread mainThread];

线程休眠相关方法

// 让线程休眠到 data 所在时间
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
// 让线程休眠 ti 秒
[NSThread sleepForTimeInterval:1.0];

参考资料

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章           查看所有文章
加:2021-10-21 12:18:02  更:2021-10-21 12:20:41 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 0:50:44-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码