iOS-RunLoop詳解(三):使用RunLoop線程?;罘桨?/h2>

iOS-RunLoop詳解(三):使用RunLoop線程?;罘桨?/h1>

如果經(jīng)常要在子線程中做事情,不使用?;?,就會一直創(chuàng)建、銷毀子線程,這樣很耗性能,所以經(jīng)常在子線程做事情最好使用線程?;睢?/p>

實現(xiàn)線程保活

創(chuàng)建線程類,表示需要經(jīng)常執(zhí)行的任務

***********************?? MJThread.h ??**************************
@interface MJThread : NSThread

@end

#import "MJThread.h"

@implementation MJThread

***********************?? MJThread.m ??**************************
- (void)dealloc
{
    NSLog(@"%s", __func__);
}

@end

***********************?? ViewController.m ??**************************
    
@implementation ViewController
    
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    // NSThread 頻繁創(chuàng)建線程
        MJThread *thread = [[MJThread alloc]initWithTarget:self selector:@selector(test) object:nil];
        [thread start];

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

// 子線程需要執(zhí)行的任務
- (void)test
{
    NSLog(@"%s %@", __func__, [NSThread currentThread]);
}
@end

RUN> ??????

我們每次點擊,都會去執(zhí)行任務:NSThread 頻繁創(chuàng)建線程

2021-05-14 14:55:19.054958+0800 Interview03-線程保活[4068:166884] -[ViewController test] <MJThread: 0x600000634900>{number = 7, name = (null)}
2021-05-14 14:55:19.056474+0800 Interview03-線程?;頪4068:166884] -[MJThread dealloc]
2021-05-14 14:55:21.627640+0800 Interview03-線程?;頪4068:166923] -[ViewController test] <MJThread: 0x60000062ddc0>{number = 8, name = (null)}
2021-05-14 14:55:21.627815+0800 Interview03-線程保活[4068:166923] -[MJThread dealloc]

可以看到,任務一執(zhí)行完,線程就釋放了.并且這樣頻繁的創(chuàng)建線程,很消耗資源

我們可以用RunLoop來延長線程的生命周期,不讓線程掛掉

// 子線程需要執(zhí)行的任務
- (void)test
{
    NSLog(@"%s %@", __func__, [NSThread currentThread]);
    
    [[NSRunLoop currentRunLoop] run];
    
    NSLog(@"%s ----end----", __func__);
}

RUN> ??????

2021-05-14 15:01:54.818875+0800 Interview03-線程?;頪4128:171606] -[ViewController test] <MJThread: 0x60000147d100>{number = 7, name = (null)}
2021-05-14 15:01:54.819344+0800 Interview03-線程?;頪4128:171606] -[ViewController test] ----end----
2021-05-14 15:01:54.821043+0800 Interview03-線程保活[4128:171606] -[MJThread dealloc]

運行一下,發(fā)現(xiàn)線程執(zhí)行完任務還是會結(jié)束線程([MJThread dealloc])

這是因為,我們雖然獲取了當前的RunLoop,并且調(diào)用run方法讓RunLoop跑起來了,而run方法底層調(diào)用的是- (BOOL)runMode:(NSRunLoopMode)mode beforeDate:(NSDate *)limitDate;方法,這個方法會把RunLoop添加到NSDefaultRunLoopMode模式下.Model中如果沒有任何Source0 , Source1 , Timer , Observer,RunLoop會立馬退出. 所以我們需要往RunLoop中添加任務,任何任務都可以.

我們需要往RunLoop中添加任務,任何任務都可以.

// 子線程需要執(zhí)行的任務
- (void)test
{
    NSLog(@"%s %@", __func__, [NSThread currentThread]);
    NSLog(@"??????????????????????????");
    // 往RunLoop里面添加Source\Timer\Observer
    [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
    [[NSRunLoop currentRunLoop] run];
    
    NSLog(@"%s ----end----", __func__);
}

RUN> ??????

2021-05-14 15:05:54.882701+0800 Interview03-線程?;頪4172:174678] -[ViewController test] <MJThread: 0x600003c90f00>{number = 7, name = (null)}
2021-05-14 15:05:54.883756+0800 Interview03-線程保活[4172:174678] ??????????????????????????

在運行線程就不會執(zhí)行完任務就掛掉了,而是執(zhí)行完任務就休眠:

每一次點擊屏幕,都是創(chuàng)建了[[MJThread alloc]initWithTarget:self selector:@selector(test) object:nil];經(jīng)過[[NSRunLoop currentRunLoop] addPort[[NSRunLoop currentRunLoop] run]; 線程進入休眠了,但是我們現(xiàn)在沒辦法喚醒線程和執(zhí)行線程任務,程序繼續(xù)修改。

上面的代碼thread是一個局部變量,每次執(zhí)行任務都會重新創(chuàng)建,所以我們把線程設(shè)置成成員屬性。

***********************?? ViewController.m ??**************************
@interface ViewController ()
@property (strong, nonatomic) MJThread *thread;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.thread = [[MJThread alloc] initWithTarget:self selector:@selector(run) object:nil];
    [self.thread start];
}

//觸碰屏幕事件
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    [self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:NO];
}

// 子線程需要執(zhí)行的任務
- (void)test
{
    NSLog(@"%s %@", __func__, [NSThread currentThread]);
    NSLog(@"??????????????????????????");
    
}

// 這個方法的目的:線程保活
- (void)run {
    NSLog(@"%s %@", __func__, [NSThread currentThread]);
    NSLog(@"---------- start -----------");
    // 往RunLoop里面添加Source\Timer\Observer
    [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
    [[NSRunLoop currentRunLoop] run];
    
    NSLog(@"%s ----end----", __func__);
}
@end

RUN> ??????

2021-05-14 15:14:16.185593+0800 Interview03-線程?;頪4221:180238] -[ViewController test] <MJThread: 0x6000005ecb40>{number = 7, name = (null)}
2021-05-14 15:14:16.185768+0800 Interview03-線程保活[4221:180238] ??????????????????????????
2021-05-14 15:14:17.821116+0800 Interview03-線程?;頪4221:180238] -[ViewController test] <MJThread: 0x6000005ecb40>{number = 7, name = (null)}
2021-05-14 15:14:17.821240+0800 Interview03-線程?;頪4221:180238] ??????????????????????????
2021-05-14 15:14:20.824925+0800 Interview03-線程保活[4221:180238] -[ViewController test] <MJThread: 0x6000005ecb40>{number = 7, name = (null)}
2021-05-14 15:14:20.825090+0800 Interview03-線程?;頪4221:180238] ??????????????????????????

上面的代碼有兩個問題:

  1. self和thread會造成循環(huán)引用,都不會釋放
  2. thread一直不會死

首先解決循環(huán)引用:

#import "ViewController.h"
#import "MJThread.h"

@interface ViewController ()
@property (strong, nonatomic) MJThread *thread;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
        //如果使用如下方式創(chuàng)建thread,self會引用thread,thread會引用self,會造成循環(huán)引用。
        //[[MJThread alloc] initWithTarget:self selector:@selector(run) object:nil];
    
        self.thread = [[MJThread alloc] initWithBlock:^{
        NSLog(@"%@----begin----", [NSThread currentThread]);
        
        // 往RunLoop里面添加Source\Timer\Observer
        [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
        
        //線程會一直阻塞這這一行,永遠不會銷毀
        [[NSRunLoop currentRunLoop] run]; 

        //當把NSRunLoop停掉之后,代碼就會從下一行往下走,這時候任務執(zhí)行完成,線程該死的時候就會死了。
        NSLog(@"%@----end----", [NSThread currentThread]);
    }];
    [self.thread start];
}

- (void)dealloc
{
    NSLog(@"%s", __func__);

    //就算把thread清空,thread也不會銷毀,因為任務還沒結(jié)束,線程就不會死。
    //self.thread = nil; 
}

運行后,在當前界面返回

RUN> ??????

2021-05-14 15:21:44.228798+0800 Interview03-線程?;頪4480:196517] <MJThread: 0x600002a04080>{number = 9, name = (null)}----begin----
2021-05-14 15:21:47.517069+0800 Interview03-線程?;頪4480:195322] -[ViewController dealloc]

可以發(fā)現(xiàn)ViewController銷毀了,但是thread還是沒被銷毀

很奇怪,控制器都釋放了,按理說控制器內(nèi)部的所有東西都應該釋放了呀,我們在ViewControllerdealloc方法中把thread置為nil:

2021-05-14 15:25:09.585499+0800 Interview03-線程?;頪4520:199781] <MJThread: 0x600003b4ab40>{number = 8, name = (null)}----begin----
2021-05-14 15:25:13.501110+0800 Interview03-線程?;頪4520:199781] -[ViewController test] <MJThread: 0x600003b4ab40>{number = 8, name = (null)}
2021-05-14 15:25:13.501288+0800 Interview03-線程?;頪4520:199781] ??????????????????????????
2021-05-14 15:25:16.475845+0800 Interview03-線程?;頪4520:199617] -[ViewController dealloc]

thread強制置為nil,thread還是沒有釋放.

 self.thread = [[MJThread alloc] initWithBlock:^{
        NSLog(@"%@----begin----", [NSThread currentThread]);
        
        // 往RunLoop里面添加Source\Timer\Observer
        [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
        
        //線程會一直阻塞這這一行,永遠不會銷毀
        [[NSRunLoop currentRunLoop] run]; 

        //當把NSRunLoop停掉之后,代碼就會從下一行往下走,這時候任務執(zhí)行完成,線程該死的時候就會死了。
        NSLog(@"%@----end----", [NSThread currentThread]);
    }];

這是因為RunLoop在 [[NSRunLoop currentRunLoop] run]這一行一直阻塞,一直不會打印----end----,這時候任務一直在進行,任務還沒有完成線程就不會死,就算在ViewController的dealloc方法里面把thread清空,thread也不會死。

如果我們想要精準的控制線程的生命周期,比如說控制器銷毀的時候,線程也銷毀,那應該怎么做呢?我們可以像下面這樣手動停止RunLoop:

- (IBAction)stop {
    // 在子線程調(diào)用stop
    [self performSelector:@selector(stopThread) onThread:self.thread withObject:nil waitUntilDone:NO];
}

// 用于停止子線程的RunLoop
- (void)stopThread
{
    // 停止RunLoop
    CFRunLoopStop(CFRunLoopGetCurrent());
    NSLog(@"%s %@", __func__, [NSThread currentThread]);
}

run> ??????

2021-05-14 15:34:46.499408+0800 Interview03-線程保活[4566:205814] <MJThread: 0x600001f1b900>{number = 9, name = (null)}----begin----
2021-05-14 15:34:55.230839+0800 Interview03-線程?;頪4566:205814] -[ViewController test] <MJThread: 0x600001f1b900>{number = 9, name = (null)}
2021-05-14 15:34:55.230956+0800 Interview03-線程保活[4566:205814] ??????????????????????????
2021-05-14 15:34:57.846728+0800 Interview03-線程?;頪4566:205814] -[ViewController stopThread] <MJThread: 0x600001f1b900>{number = 9, name = (null)}
2021-05-14 15:35:14.203913+0800 Interview03-線程?;頪4566:205687] -[ViewController dealloc]

stopRunLoop 雖然執(zhí)行了,并且ViewController 也已經(jīng)銷毀了,但是thread 仍然沒有銷毀,這是為什么呢?

線程不會死的原因就是有個RunLoop一直在運行,線程一直有任務做,所以想讓線程死掉,就把RunLoop停掉,當把RunLoop停掉之后,代碼就會從 [[NSRunLoop currentRunLoop] run]往下走,當線程執(zhí)行完任務后,線程該死的時候(當前控制器銷毀后)就會死了。

我們看run方法的解釋:

it runs the receiver in the NSDefaultRunLoopMode by repeatedly invoking runMode:beforeDate:.
In other words, this method effectively begins an infinite loop that processes data from the run loop’s input sources and timers.

翻譯過來就是:
它通過反復調(diào)用runMode:beforeDate:在NSDefaultRunLoopMode中運行接收器。換句話說,這個方法有效地開始了一個無限循環(huán),處理來自運行循環(huán)的輸入源和計時器的數(shù)據(jù)。

可以看出,通過run方法運行的RunLoop是無法停止的,它專門用于開啟一個永不銷毀的線程(NSRunLoop)。

既然這樣,那我們可以模仿run方法,寫個while循環(huán),內(nèi)部也調(diào)用runMode:beforeDate:方法,如下:

while (!weakSelf.isStoped) {
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
//while的條件判斷中要使用weakSelf,不然self強引用thread,thread強引用block,block強引用self,產(chǎn)生循環(huán)引用

不使用run方法,我們就能停掉RunLoop了,停掉RunLoop系統(tǒng)有提供API是CFRunLoopStop(CFRunLoopGetCurrent()),但是這個API不能在ViewController的dealloc方法里面寫,因為ViewController的dealloc方法是在主線程調(diào)用的,我們要保證在子線程調(diào)用CFRunLoopStop(CFRunLoopGetCurrent())。

#import "ViewController.h"
#import "MJThread.h"

@interface ViewController ()
@property (strong, nonatomic) MJThread *thread;
@property (assign, nonatomic, getter=isStoped) BOOL stopped;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    __weak typeof(self) weakSelf = self;

    self.stopped = NO;
    self.thread = [[MJThread alloc] initWithBlock:^{
        NSLog(@"%@----begin----", [NSThread currentThread]);

        // 往RunLoop里面添加Source\Timer\Observer
        [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
        while (!weakSelf.isStoped) {
            [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
        }

        NSLog(@"%@----end----", [NSThread currentThread]);

        // NSRunLoop的run方法是無法停止的,它專門用于開啟一個永不銷毀的線程(NSRunLoop)
        //        [[NSRunLoop currentRunLoop] run];
        /*
         it runs the receiver in the NSDefaultRunLoopMode by repeatedly invoking runMode:beforeDate:.
         In other words, this method effectively begins an infinite loop that processes data from the run loop’s input sources and timers
         */

    }];
    [self.thread start];
}



- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    [self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:NO];
}

// 子線程需要執(zhí)行的任務
- (void)test
{
    NSLog(@"%s %@", __func__, [NSThread currentThread]);
    NSLog(@"??????????????????????????");
}

- (IBAction)stop {
    // 在子線程調(diào)用stop
    [self performSelector:@selector(stopThread) onThread:self.thread withObject:nil waitUntilDone:NO];
}

// 用于停止子線程的RunLoop
- (void)stopThread
{
    // 設(shè)置標記為NO
    self.stopped = YES;
    
    // 停止RunLoop
    CFRunLoopStop(CFRunLoopGetCurrent());
    NSLog(@"%s %@", __func__, [NSThread currentThread]);
}

- (void)dealloc
{
    NSLog(@"%s", __func__);
    //就算把thread清空,thread也不會銷毀,因為任務還沒結(jié)束,線程就不會死。
    self.thread = nil;
    
//    [self stop];
}

@end

RUN> ??????

2021-05-14 15:39:47.441947+0800 Interview03-線程保活[4606:209299] <MJThread: 0x600000f47580>{number = 8, name = (null)}----begin----
2021-05-14 15:39:48.914864+0800 Interview03-線程?;頪4606:209299] -[ViewController test] <MJThread: 0x600000f47580>{number = 8, name = (null)}
2021-05-14 15:39:48.915021+0800 Interview03-線程保活[4606:209299] ??????????????????????????
2021-05-14 15:39:50.900749+0800 Interview03-線程?;頪4606:209299] -[ViewController stopThread] <MJThread: 0x600000f47580>{number = 8, name = (null)}
2021-05-14 15:39:50.901004+0800 Interview03-線程保活[4606:209299] <MJThread: 0x600000f47580>{number = 8, name = (null)}----end----
2021-05-14 15:39:59.322333+0800 Interview03-線程?;頪4606:209161] -[ViewController dealloc]
2021-05-14 15:39:59.322455+0800 Interview03-線程?;頪4606:209161] -[MJThread dealloc]

上面要使用weakself,不然self強引用thread,thread強引用block,block強引用self,產(chǎn)生循環(huán)引用(使用weakself之后,就是self強引用thread,thread強引用block,block弱引用self,不會產(chǎn)生循環(huán)引用)。

運行代碼,進入界面,打?。?/p>

2021-05-14 15:39:47.441947+0800 Interview03-線程?;頪4606:209299] <MJThread: 0x600000f47580>{number = 8, name = (null)}----begin----

說明線程開始工作了。

點擊空白,打?。?/p>

2021-05-14 15:42:22.608171+0800 Interview03-線程?;頪4606:211084] -[ViewController test] <MJThread: 0x600000f38540>{number = 9, name = (null)}
2021-05-14 15:42:22.610916+0800 Interview03-線程保活[4606:211084] ??????????????????????????

說明RunLoop接收到事件,開始處理事件。

點擊stop打?。?/p>

2021-05-14 15:42:35.860716+0800 Interview03-線程?;頪4606:211084] -[ViewController stopThread] <MJThread: 0x600000f38540>{number = 9, name = (null)}
2021-05-14 15:42:35.861011+0800 Interview03-線程?;頪4606:211084] <MJThread: 0x600000f38540>{number = 9, name = (null)}----end----

可以看出,執(zhí)行了CFRunLoopStop,并且線程任務完成,打印了----end----。

點擊stop之后再退出當前VC,打印:

2021-05-14 15:44:08.504631+0800 Interview03-線程?;頪4606:209161] -[ViewController dealloc]
2021-05-14 15:44:08.504806+0800 Interview03-線程保活[4606:209161] -[MJThread dealloc]

可以發(fā)現(xiàn),當前VC和thread都被銷毀了。

上面代碼還有一個問題,就是我們每次都要先點擊停止再返回當前VC,這樣很麻煩,可能你會說可以把[self stop]方法寫在ViewController的dealloc方法里面,試了下,發(fā)現(xiàn)報錯壞內(nèi)存訪問:

image-20210514155504725

這是為什么呢?這就是我們上面講的waitUntilDone造成的.我們在[self performSelector:@selector(stopRunLoop) onThread:self.thread withObject:nil waitUntilDone:NO];中把waitUntilDone設(shè)置為NO,就表示在子線程執(zhí)行的stopRunLoop函數(shù)和在主線程執(zhí)行的- (IBAction)stop函數(shù)是同時執(zhí)行的.一旦- (IBAction)stop函數(shù)先執(zhí)行完,那么ViewControllerdealloc函數(shù)也會立馬執(zhí)行完畢,ViewController就會釋放.這時候再去執(zhí)行stopRunLoop就會報壞內(nèi)存訪問,因為ViewController已經(jīng)釋放了.為什么會崩潰到[[NSRunLoop currentRunLoop]runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];這一行呢?

現(xiàn)在你應該明白為什么會在RunLoop那行代碼報壞內(nèi)存訪問錯誤了吧!

解決辦法也很簡單,dealloc方法里面調(diào)用[self stop],并且將上面NO改成YES。

- (IBAction)stop {
    // 在子線程調(diào)用stop
    [self performSelector:@selector(stopThread) onThread:self.thread withObject:nil waitUntilDone:YES];
}
- (void)dealloc
{
    NSLog(@"%s", __func__);
    
   [self stop];
}

運行代碼,直接返回當前VC,打印:

2021-05-14 15:59:38.055083+0800 Interview03-線程?;頪4787:225673] -[ViewController dealloc]
2021-05-14 15:59:38.055307+0800 Interview03-線程?;頪4787:225794] -[ViewController stopThread] <MJThread: 0x600000a8dd00>{number = 8, name = (null)}

我們點擊返回退出控制器后ViewController釋放了,但是沒有看到線程釋放

其實那個RunLoop的確停掉了,但是停掉之后,他會再次來到while循環(huán)判斷條件:

我們在while循環(huán)中打一個斷點:

image-20210514160136836

這時候當前控制器已經(jīng)被銷毀,weakSelf指針已經(jīng)被清空,這時候!nil獲取的就是YES,所以會再次進入循環(huán)體啟動RunLoop,RunLoop又跑起來了,線程又有事情干了,所以線程不會銷毀。

解決辦法:

while (weakSelf && !weakSelf.isStoped) {
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}

再次運行項目,返回當前VC

2021-05-14 16:03:29.471773+0800 Interview03-線程?;頪4863:229852] <MJThread: 0x600000452e40>{number = 9, name = (null)}----end----
2021-05-14 16:03:29.472196+0800 Interview03-線程保活[4863:229852] -[MJThread dealloc]

再次運行項目,點擊暫停,返回當前VC,這時候又崩了

image-20210514160513785

點擊暫停之后RunLoop肯定停掉了,RunLoop停掉后,這時候的線程就不能用了,runloop停止掉后它的任務就執(zhí)行完了,線程的生命周期已經(jīng)結(jié)束了,這時候它已經(jīng)不能再執(zhí)行任務了.但是這時候thread還沒銷毀(還沒調(diào)用dealloc),因為thread還被self引用著,我們點擊返回按鈕,又讓子線程去執(zhí)行stopRunLoop任務就會報錯,這時候訪問一個不能用的thread就會報壞內(nèi)存訪問錯誤。

解決辦法也很簡單,暫停RunLoop后把thread指針置為nil,并且如果發(fā)現(xiàn)子線程為nil就不在子線程執(zhí)行任務了。

image-20210514160945789

最后的完整代碼

#import "ViewController.h"
#import "MJThread.h"

@interface ViewController ()
@property (strong, nonatomic) MJThread *thread;
@property (assign, nonatomic, getter=isStoped) BOOL stopped;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    __weak typeof(self) weakSelf = self;

    self.stopped = NO;
    self.thread = [[MJThread alloc] initWithBlock:^{
        NSLog(@"%@----begin----", [NSThread currentThread]);

        // 往RunLoop里面添加Source\Timer\Observer
        [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
        while (weakSelf && !weakSelf.isStoped) {
            [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
        }

        NSLog(@"%@----end----", [NSThread currentThread]);

        // NSRunLoop的run方法是無法停止的,它專門用于開啟一個永不銷毀的線程(NSRunLoop)
        //        [[NSRunLoop currentRunLoop] run];
        /*
         it runs the receiver in the NSDefaultRunLoopMode by repeatedly invoking runMode:beforeDate:.
         In other words, this method effectively begins an infinite loop that processes data from the run loop’s input sources and timers
         */

    }];
    [self.thread start];
}



- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    [self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:NO];
}

// 子線程需要執(zhí)行的任務
- (void)test
{
    NSLog(@"%s %@", __func__, [NSThread currentThread]);
    NSLog(@"??????????????????????????");
}

- (IBAction)stop {
    // 在子線程調(diào)用stop
    if (!self.thread) return;
    [self performSelector:@selector(stopThread) onThread:self.thread withObject:nil waitUntilDone:YES];
}

// 用于停止子線程的RunLoop
- (void)stopThread
{
    // 設(shè)置標記為NO
    self.stopped = YES;
    
    // 停止RunLoop
    CFRunLoopStop(CFRunLoopGetCurrent());
    self.thread = nil;
    NSLog(@"%s %@", __func__, [NSThread currentThread]);
}

- (void)dealloc
{
    NSLog(@"%s", __func__);
    
    [self stop];
}

@end
特別備注

本系列文章總結(jié)自MJ老師在騰訊課堂iOS底層原理班(下)/OC對象/關(guān)聯(lián)對象/多線程/內(nèi)存管理/性能優(yōu)化,相關(guān)圖片素材均取自課程中的課件。如有侵權(quán),請聯(lián)系我刪除,謝謝!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

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

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