iOS多線程之NSThread

NSThread是官方提供的一套面向?qū)ο蟮妮p量級(jí)多線程開發(fā)技術(shù)。使用較為簡單,不需要過多地操作線程的行為配置,但是仍然需要開發(fā)者自己處理線程的生命周期。相比于C語言中的pthread相關(guān)接口,NSThread易用性更強(qiáng)。

一、NSThread開啟新線程的方式
1、構(gòu)造器方式

NSThread中提供了如下兩個(gè)類方法:

+ (void)detachNewThreadWithBlock:(void (^)(void))block)
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument

兩個(gè)方法作用類似,都是自動(dòng)開啟新線程,執(zhí)行任務(wù)。

2、初始化方式

通過手動(dòng)調(diào)用初始化方法,可以獲取到線程對象,從而更方便地對線程進(jìn)行配置以及獲取線程信息。示例如下:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    NSLog(@"%@", [NSThread currentThread]);
    
    NSThread *thread1 = [[NSThread alloc] initWithBlock:^{
        NSLog(@"1_%@", [NSThread currentThread]);
    }];
    [thread1 start];
    
    
    NSThread *thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(test) object:nil];
    [thread2 start];
}

- (void)test {
    NSLog(@"2_%@", [NSThread currentThread]);
}

運(yùn)行代碼,控制臺(tái)輸出如下:

2021-12-23 14:12:11.726978+0800 MyProject[38641:550626] <_NSMainThread: 0x6000039c8080>{number = 1, name = main}
2021-12-23 14:12:11.727314+0800 MyProject[38641:550803] 1_<NSThread: 0x600003983500>{number = 7, name = (null)}
2021-12-23 14:12:11.727395+0800 MyProject[38641:550804] 2_<NSThread: 0x600003983340>{number = 8, name = (null)}

需要注意的是,初始化方式創(chuàng)建線程,需要調(diào)用start方法來啟動(dòng)線程任務(wù)。

3、自定義線程

通過繼承NSThread可以創(chuàng)建自定義的線程,自定義線程通過內(nèi)部main函數(shù)設(shè)定要執(zhí)行的任務(wù)。示例如下:

#import "MyThread.h"
@implementation MyThread

- (void)main {
    NSLog(@"自定義線程:%@", [NSThread currentThread]);
}

@end

自定義線程的使用方法如下:

MyThread *thread = [[MyThread alloc] init];
[thread start];
4、performSelector

只要是NSObject的子類或?qū)嵗伎梢酝ㄟ^調(diào)用方法進(jìn)入子線程和主線程,其實(shí)這些方法開辟的子線程,也是NSThread的一種體現(xiàn)方式。常用方法如下:

// 當(dāng)前線程,延時(shí)1s執(zhí)行
[self performSelector:@selector(text) withObject:nil afterDelay:1];

該方法也響應(yīng)了Objective-C的動(dòng)態(tài)性:延時(shí)到運(yùn)行時(shí)才綁定方法。需要注意的是,帶afterDelay的延時(shí)函數(shù),會(huì)在內(nèi)部創(chuàng)建一個(gè)NSTimer,然后添加到當(dāng)前線程的runloop中。如果當(dāng)前線程沒有開啟Runloop,則方法會(huì)失效,例如:

dispatch_async(dispatch_get_global_queue(0, 0), ^{
    [self performSelector:@selector(test) withObject:nil afterDelay:0];
});

這里test方法是不會(huì)執(zhí)行的,因?yàn)?br> - (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay
這個(gè)方法要?jiǎng)?chuàng)建提交任務(wù)到runloop上,而GCD底層創(chuàng)建的線程是默認(rèn)不開啟對應(yīng)的runloop的,所以test不會(huì)執(zhí)行。
如果將dispatch_get_global_queue改為主隊(duì)列,由于主隊(duì)列所在的主線程是默認(rèn)開啟runloop的,則回去執(zhí)行test。如果將dispatch_async改為dispatch_sync,同步在當(dāng)前線程執(zhí)行,如果當(dāng)前線程是主線程,則test可以執(zhí)行。

// 回到主線程
// waitUntilDone YES - 立刻執(zhí)行 NO - 等待當(dāng)前Runloop空閑后執(zhí)行
[self performSelectorOnMainThread:@selector(test) withObject:nil waitUntilDone:YES];
// 開辟子線程
[self performSelectorInBackground:@selector(test) withObject:nil];
// 在指定的線程中執(zhí)行任務(wù)
// 任務(wù)執(zhí)行依賴Runloop,所以線程Runloop必須開啟
[self performSelector:@selector(test) onThread:[NSThread currentThread] withObject:nil waitUntilDone:YES];
二、相關(guān)屬性與方法
1、獲取線程信息的類方法、屬性
// 獲取當(dāng)前線程N(yùn)SThread對象
@property (class, readonly, strong) NSThread *currentThread;
// 獲取主線程N(yùn)SThread對象
@property (class, readonly, strong) NSThread *mainThread;
// 當(dāng)前應(yīng)用程序是否支持多線程
+ (BOOL)isMultiThreaded;
// 當(dāng)前是否為主線程
@property (class, readonly) BOOL isMainThread;
// 獲取當(dāng)前線程的優(yōu)先級(jí)
+ (double)threadPriority;
// 當(dāng)前線程執(zhí)行代碼的堆棧地址
@property (class, readonly, copy) NSArray<NSNumber *> *callStackReturnAddresses;
// 當(dāng)前線程執(zhí)行代碼的堆棧信息
@property (class, readonly, copy) NSArray<NSString *> *callStackSymbols;

2、控制線程行為的類方法

// 使當(dāng)前線程休眠到指定時(shí)間
+ (void)sleepUntilDate:(NSDate *)date;
// 使當(dāng)前線程休眠一定時(shí)間
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
// 結(jié)束當(dāng)前線程
+ (void)exit;
// 設(shè)置當(dāng)前線程的優(yōu)先級(jí)
+ (BOOL)setThreadPriority:(double)p;

3、其他常用實(shí)例屬性、方法

// 線程名稱
@property (nullable, copy) NSString *name;
// 堆棧大小
@property NSUInteger stackSize;
// 是否為主線程
@property (readonly) BOOL isMainThread;
// 是否正在執(zhí)行
@property (readonly, getter=isExecuting) BOOL executing;
// 是否執(zhí)行完成
@property (readonly, getter=isFinished) BOOL finished;
// 是否已經(jīng)取消
@property (readonly, getter=isCancelled) BOOL cancelled;
// 取消線程(線程不會(huì)立即停止執(zhí)行,需要開發(fā)者根據(jù)cancelled屬性做邏輯處理)
- (void)cancel;
// 啟動(dòng)線程
- (void)start;
// 線程主體,自定義的NSThread子類通過重寫該方法來指定要執(zhí)行的任務(wù)
- (void)main;
三、相關(guān)通知

系統(tǒng)定義了幾個(gè)線程相關(guān)的通知,我們可以通過監(jiān)聽來關(guān)注多線程的運(yùn)行狀態(tài),具體名稱以及發(fā)送時(shí)機(jī)如下:

// 將進(jìn)入多線程運(yùn)行模式
FOUNDATION_EXPORT NSNotificationName const NSWillBecomeMultiThreadedNotification;
// 已經(jīng)進(jìn)入多線程運(yùn)行模式
FOUNDATION_EXPORT NSNotificationName const NSDidBecomeSingleThreadedNotification;
// 某個(gè)線層結(jié)束
FOUNDATION_EXPORT NSNotificationName const NSThreadWillExitNotification;
四、NSThread+Runloop實(shí)現(xiàn)常駐線程

NSThread在實(shí)際開發(fā)中,比較常用到的場景就是實(shí)現(xiàn)常駐線程。
由于每次開辟子線程都會(huì)消耗CPU,所以頻繁開啟子線程會(huì)消耗大量CPU,而且創(chuàng)建的子線程都是任務(wù)執(zhí)行后,就會(huì)被釋放,不能再次利用。
常駐線程,就是一個(gè)使用完以后,不會(huì)釋放,可以再次利用的線程。
使用NSThead+Runloop實(shí)現(xiàn)常駐線程,示例代碼如下:

- (void)viewDidLoad {
    [super viewDidLoad];

    [self performSelector:@selector(test) onThread:[self shareThread] withObject:nil waitUntilDone:NO];
}

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

// 創(chuàng)建一個(gè)NSThread單例
- (NSThread *)shareThread {
    static NSThread *shareThread = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        shareThread = [[NSThread alloc] initWithTarget:self selector:@selector(threadTest) object:nil];
        [shareThread setName:@"MyThread"];
        [shareThread start];
    });
    return shareThread;
}

- (void)threadTest {
    @autoreleasepool {
        NSRunLoop *runloop = [NSRunLoop currentRunLoop];
        [runloop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
        [runloop run];
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 基本概念 進(jìn)程: 一個(gè)具有一定獨(dú)立功能的程序關(guān)于某個(gè)數(shù)據(jù)集合的一次運(yùn)行活動(dòng)??梢岳斫獬梢粋€(gè)運(yùn)行中的應(yīng)用程序。線程:...
    charlotte2018閱讀 562評(píng)論 0 4
  • NSThread是iOS中底層的線程類,相比GCD和NSOperation更加輕量級(jí),也提供了更為靈活的使用方式,...
    icetime17閱讀 417評(píng)論 0 1
  • 1. 線程的概念 首先簡單敘述一下這兩個(gè)概念,我們在電腦上單獨(dú)運(yùn)行的每個(gè)程序就是一個(gè)獨(dú)立的進(jìn)程,通常進(jìn)程之間是相互...
    大成小棧閱讀 521評(píng)論 0 0
  • NSThread 特點(diǎn):更加面向?qū)ο蠛唵我子?,可直接操作線程對象使用語言:OC語言使用頻率:偶爾使用線程生命周期:...
    BWLi420閱讀 610評(píng)論 0 1
  • 0. 前言 NSThread 是 iOS 多線程當(dāng)中最基礎(chǔ)最輕量的多線程技術(shù),但是需要自行管理線程的生命周期和同步...
    24小時(shí)營業(yè)閱讀 1,327評(píng)論 0 0

友情鏈接更多精彩內(nèi)容