iOS 多线程基础之 NSThread
前言
通常在 iOS 中,有三種比較常見(jiàn)的方式實(shí)現(xiàn)多線程,分別是 NSThread,GCD 和 NSOperation。本文主要介紹它們當(dāng)中使用頻率較低的 NSThread。
文章目錄
- 前言
- NSThread
- 1. 線程創(chuàng)建
- 方法一:常規(guī)方法創(chuàng)建新線程
- 方法二:detachNewThread 系列類方法
- 方法三:NSObject + NSThreadPerformAdditions 分類中提供的方法
- 2. 線程優(yōu)先級(jí)
- 優(yōu)先級(jí)實(shí)驗(yàn)
- 新的優(yōu)先級(jí)屬性
- 3. 線程間通信
- 線程間通信實(shí)例
- 4. 常用屬性和方法
- 改變線程狀態(tài)的方法
- 判斷線程狀態(tài)的方法
- 主線程判斷的方法
- 執(zhí)行線程任務(wù)的方法
- NSThread 常用類方法
- 線程休眠相關(guān)方法
- 參考資料
NSThread
NSThread 是 Foundation 框架中封裝的線程類,其對(duì)象就表示程序的一個(gè)線程。
1. 線程創(chuàng)建
我們有下面三種方法可以創(chuàng)建新線程去執(zhí)行任務(wù)。
方法一:常規(guī)方法創(chuàng)建新線程
這種方法比較常規(guī),通過(guò)對(duì)象創(chuàng)建和初始化方法進(jìn)行新線程的創(chuàng)建。雖然寫(xiě)代碼的步驟看起來(lái)比較復(fù)雜,但是可以獲取到線程的對(duì)象,可以對(duì)線程進(jìn)行名稱、優(yōu)先級(jí)之類的個(gè)性化修改。
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadRun) object:nil];NSThread *thread = [[NSThread alloc] initWithBlock:^{NSLog(@"Run %@", [NSThread currentThread]); }];// 需要手動(dòng)調(diào)用 start 方法 [thread start];threadRun 方法表示線程將要執(zhí)行的任務(wù)
- (void)threadRun {NSLog(@"Run %@", [NSThread currentThread]); }方法二:detachNewThread 系列類方法
我們可以通過(guò) detachNewThread 系列的類方法創(chuàng)建新線程來(lái)執(zhí)行任務(wù),代碼會(huì)比較簡(jiǎn)潔。但是這些方法都是沒(méi)有返回值的,這就意味著我們無(wú)法獲取到新創(chuàng)建的線程,進(jìn)行無(wú)法修改線程設(shè)置。
[NSThread detachNewThreadSelector:@selector(threadRun) toTarget:self withObject:nil];[NSThread detachNewThreadWithBlock:^{NSLog(@"Run %@", [NSThread currentThread]); }];方法三:NSObject + NSThreadPerformAdditions 分類中提供的方法
NSObject + NSThreadPerformAdditions 分類中提供的 performSelectorInBackground: withObject: 方法也可以創(chuàng)建新線程執(zhí)行任務(wù),它的使用情況和方法二相似。
[self performSelectorInBackground:@selector(threadRun) withObject:nil];2. 線程優(yōu)先級(jí)
線程是具有優(yōu)先級(jí)的,線程的優(yōu)先級(jí)代表 CPU 調(diào)度的優(yōu)先級(jí),優(yōu)先級(jí)高的線程更容易得到 CPU 資源執(zhí)行任務(wù)。對(duì)于 NSThread 對(duì)象,我們可以通過(guò) threadPriority 屬性來(lái)修改它的優(yōu)先級(jí),優(yōu)先級(jí)的取值范圍為 [0.0,1.0][0.0, 1.0][0.0,1.0],默認(rèn)優(yōu)先級(jí)為 0.5。
優(yōu)先級(jí)實(shí)驗(yàn)
我們創(chuàng)建三個(gè)線程,優(yōu)先級(jí)分別為 1.0 / 0.1 / 默認(rèn),讓它們執(zhí)行同一個(gè)任務(wù),根據(jù)控制臺(tái)輸出結(jié)果,查看各優(yōu)先級(jí)線程的任務(wù)執(zhí)行情況。
// 設(shè)置線程 A 的優(yōu)先級(jí)為 1.0 NSThread *threadA = [[NSThread alloc] initWithTarget:self selector:@selector(threadRun) object:nil]; threadA.name = @"threadA"; threadA.threadPriority = 1.0; [threadA start];// 設(shè)置線程 B 的優(yōu)先級(jí)為 0.1 NSThread *threadB = [[NSThread alloc] initWithTarget:self selector:@selector(threadRun) object:nil]; threadB.name = @"threadB"; threadB.threadPriority = 0.1; [threadB start];// 讓線程 C 保持默認(rèn)優(yōu)先級(jí)(0.5) NSThread *threadC = [[NSThread alloc] initWithTarget:self selector:@selector(threadRun) object:nil]; threadC.name = @"threadC"; [threadC start];// 線程要執(zhí)行的任務(wù) - (void)threadRun {for (int i = 0; i < 100; i++) {NSLog(@"Run %@ - %d", [NSThread currentThread].name, i);} }根據(jù)上面例子的執(zhí)行結(jié)果可以看出來(lái),優(yōu)先級(jí)高的線程能更快地完成它需要執(zhí)行的任務(wù)。
新的優(yōu)先級(jí)屬性
從官方的注釋中,我們發(fā)現(xiàn) threadPriority 屬性在將來(lái)會(huì)被棄用,用 qualityOfService 作為新的線程優(yōu)先級(jí)。
// 新版的線程優(yōu)先級(jí) @property NSQualityOfService qualityOfService;typedef NS_ENUM(NSInteger, NSQualityOfService) {// 和圖形處理相關(guān)的任務(wù),比如滾動(dòng)和動(dòng)畫(huà)NSQualityOfServiceUserInteractive = 0x21,// 用戶請(qǐng)求的任務(wù),但是不需要精確到毫秒級(jí)。例如,用戶請(qǐng)求打開(kāi)電子郵件App來(lái)查看郵件NSQualityOfServiceUserInitiated = 0x19,// 周期性的用戶請(qǐng)求任務(wù)。比如,電子郵件App可能被設(shè)置成每5分鐘自動(dòng)檢測(cè)新郵件NSQualityOfServiceUtility = 0x11,// 后臺(tái)任務(wù),對(duì)這些任務(wù)用戶可能并不會(huì)察覺(jué)NSQualityOfServiceBackground = 0x09,// 默認(rèn)的優(yōu)先級(jí),優(yōu)先級(jí)在 Utility 之前NSQualityOfServiceDefault = -1 } API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0));3. 線程間通信
通常線程間通信主要有下面兩種情形:
- 一個(gè)線程的數(shù)據(jù)傳遞給另一個(gè)線程
- 一個(gè)線程的任務(wù)執(zhí)行完后,轉(zhuǎn)到另一個(gè)線程繼續(xù)執(zhí)行任務(wù)
與 NSThread 有關(guān)的線程間通信實(shí)現(xiàn)的方法在 NSObject + NSThreadPerformAdditions 分類中,常用的是下面兩個(gè)方法。
// 到主線程執(zhí)行指定任務(wù) - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;// 到指定線程執(zhí)行指定任務(wù) - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait線程間通信實(shí)例
這里用一個(gè)經(jīng)典實(shí)例,來(lái)對(duì)線程間通信進(jìn)行講解。我們要在網(wǎng)上下載圖片,然后展示在手機(jī)屏幕上顯示。為了不阻塞主線程,我們需要異步進(jìn)行圖片的下載,又因?yàn)?iOS 不能在子線程更新 UI,我們需要在圖片下載完成之后回到主線程將下載好的圖片更新到 UIImageView 上。
@interface ViewController ()// 用于展示圖片 @property (nonatomic, strong) UIImageView *imageView;@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];[self.view addSubview:self.imageView];// 設(shè)置約束[self.imageView mas_makeConstraints:^(MASConstraintMaker *make) {make.center.equalTo(self.view);make.size.mas_equalTo(CGSizeMake(216, 384));}];// 創(chuàng)建線程NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(downloadImage) object:nil];// 啟動(dòng)線程[thread start]; }#pragma mark - Actions// 后臺(tái)線程處理方法 - (void)downloadImage {// 耗時(shí)操作,下載網(wǎng)絡(luò)圖片NSURL *url = [NSURL URLWithString:@"https://pics3.baidu.com/feed/b151f8198618367afc010b4f3e2a1dd2b21ce573.jpeg?token=a80568b00de1ae2814069f2f70241a96"];NSData *imgData = [NSData dataWithContentsOfURL:url];// 執(zhí)行耗時(shí)操作完成,返回主線程處理[self performSelectorOnMainThread:@selector(updateUIWithData:) withObject:imgData waitUntilDone:YES]; }// 需要在主線程執(zhí)行的操作 - (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; }@end4. 常用屬性和方法
因?yàn)榍懊嫔厦嫣岬降?NSThread 使用方式都有更加便捷的 GCD 函數(shù)代替,所以相比之下,下面這些方法反而會(huì)更加常用。
改變線程狀態(tài)的方法
// 啟動(dòng)線程執(zhí)行任務(wù),創(chuàng)建 -> 就緒 [thread start]; // 標(biāo)記線程為 cancelled 狀態(tài) [thread cancel]; // 退出線程執(zhí)行, -> 終止 [NSThread exit];判斷線程狀態(tài)的方法
// 判斷任務(wù)是否被取消 BOOL isCancelled = [thread isCancelled]; // 判斷任務(wù)是否正在執(zhí)行 BOOL isExecuting = [thread isExecuting]; // 判斷任務(wù)是否執(zhí)行完成 BOOL isFinished = [thread isFinished];主線程判斷的方法
// 判斷 thread 是否為主線程 BOOL isMainThread = [thread isMainThread];執(zhí)行線程任務(wù)的方法
// 在當(dāng)前線程中執(zhí)行 thread 對(duì)象的任務(wù) [thread main];NSThread 常用類方法
// 判斷是否處在多線程環(huán)境 [NSThread isMultiThreaded];// 獲取當(dāng)前線程 [NSThread currentThread]// 獲取主線程 [NSThread mainThread];線程休眠相關(guān)方法
// 讓線程休眠到 data 所在時(shí)間 [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]]; // 讓線程休眠 ti 秒 [NSThread sleepForTimeInterval:1.0];參考資料
- NSThread | Apple Developer Documentation
總結(jié)
以上是生活随笔為你收集整理的iOS 多线程基础之 NSThread的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: iOS - 数据持久化之 FMDB 的使
- 下一篇: 有 OC 经验的程序员快速学习 Swif