iOS-RunLoop2-線程保活

如果經(jīng)常要在子線程中做事情,不使用保活,就會(huì)一直創(chuàng)建、銷毀子線程,這樣很耗性能的,所以經(jīng)常在子線程做事情最好使用線程保活,比如AFN2.X就使用RunLoop實(shí)現(xiàn)了線程保活。

一. 實(shí)現(xiàn)線程保活

為了監(jiān)控線程生命周期我們自定義MJThread繼承于NSThread,重寫(xiě)其dealloc方法,實(shí)現(xiàn)如下代碼:

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

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

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    //self和thread會(huì)造成循環(huán)引用
    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];
    //waitUntilDone:YES 等到子線程任務(wù)執(zhí)行完再執(zhí)行下面NSLog
    //NO 不用等到子線程執(zhí)行完再執(zhí)行下面NSLog(下面NSLog在主線程,test在子線程,同時(shí)執(zhí)行)
    NSLog(@"123");
}

// 子線程需要執(zhí)行的任務(wù)
- (void)test
{
    NSLog(@"%s %@", __func__, [NSThread currentThread]);
    // 沒(méi)打印dealloc,也沒(méi)打印----end----
    // -[ViewController test] <MJThread: 0x600000a8fec0>{number = 3, name = (null)}
}

// 這個(gè)方法的目的:線程保活
- (void)run {
    NSLog(@"%s %@", __func__, [NSThread currentThread]);
    
    // 往RunLoop里面添加Source\Timer\Observer,Port相關(guān)的是Source1事件
    [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
    
    //添加了一個(gè)Source1,但是這個(gè)Source1也沒(méi)啥事,所以線程在這里就休眠了,不會(huì)往下走,不會(huì)打印----end----
    //如果不添加Source\Timer\Observer,RunLoop沒(méi)有任何事件處理RunLoop就會(huì)立馬退出,打印----end----
    [[NSRunLoop currentRunLoop] run];
    
    NSLog(@"%s ----end----", __func__);
}

@end

上面代碼,如果只寫(xiě) [[NSRunLoop currentRunLoop] run],不添加Port,RunLoop沒(méi)有任何事件處理,那么RunLoop就會(huì)立馬退出,會(huì)打印----end----,如果添加Port,并且[[NSRunLoop currentRunLoop] run],如下:

[[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];

這樣NSRunLoop里面有事件(雖然不用處理什么),就不會(huì)退出了。線程在[[NSRunLoop currentRunLoop] run]這一行就休眠了,不會(huì)往下執(zhí)行打印----end----了,如果有其他事情,線程會(huì)再次被喚醒,處理事情。

上面的代碼有兩個(gè)問(wèn)題:

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

首先解決循環(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會(huì)引用thread,thread會(huì)引用self,會(huì)造成循環(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];
        
        //線程會(huì)一直阻塞這這一行,永遠(yuǎn)不會(huì)銷毀
        [[NSRunLoop currentRunLoop] run]; 

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

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

    //就算把thread清空,thread也不會(huì)銷毀,因?yàn)槿蝿?wù)還沒(méi)結(jié)束,線程就不會(huì)死。
    //self.thread = nil; 
}

運(yùn)行后,在當(dāng)前界面返回,打印:

-[ViewController dealloc]

可以發(fā)現(xiàn)ViewController銷毀了,但是thread還是沒(méi)被銷毀,為什么呢?

這是因?yàn)镽unLoop在 [[NSRunLoop currentRunLoop] run]這一行一直阻塞,一直不會(huì)打印----end----,這時(shí)候任務(wù)一直在進(jìn)行,任務(wù)還沒(méi)有完成線程就不會(huì)死,就算在ViewController的dealloc方法里面把thread清空,thread也不會(huì)死。

那怎么解決線程不會(huì)死的問(wèn)題呢?
線程不會(huì)死的原因就是有個(gè)RunLoop一直在運(yùn)行,線程一直有任務(wù)做,所以想讓線程死掉,就把RunLoop停掉,當(dāng)把RunLoop停掉之后,代碼就會(huì)從 [[NSRunLoop currentRunLoop] run]往下走,當(dāng)線程執(zhí)行完任務(wù)后,線程該死的時(shí)候(當(dāng)前控制器銷毀后)就會(huì)死了。

我們看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.

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

可以看出,通過(guò)run方法運(yùn)行的RunLoop是無(wú)法停止的,它專門(mén)用于開(kāi)啟一個(gè)永不銷毀的線程(NSRunLoop)。

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

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

不使用run方法,我們就能停掉RunLoop了,停掉RunLoop系統(tǒng)有提供API是CFRunLoopStop(CFRunLoopGetCurrent()),但是這個(gè)API不能在ViewController的dealloc方法里面寫(xiě),因?yàn)閂iewController的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;
    
    //如果使用如下方式創(chuàng)建thread,self會(huì)引用thread,thread會(huì)引用self,會(huì)造成循環(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];
        //要使用weakself,不然self強(qiáng)引用thread,thread強(qiáng)引用block,block強(qiáng)引用self,產(chǎn)生循環(huán)引用。
        while (!weakSelf.isStoped) {
            //beforeDat:過(guò)期時(shí)間,傳入distantFuture遙遠(yuǎn)的未來(lái),就是永遠(yuǎn)不會(huì)過(guò)期
            [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
        }
        
        NSLog(@"%@----end----", [NSThread currentThread]);
        
        // NSRunLoop的run方法是無(wú)法停止的,它專門(mén)用于開(kāi)啟一個(gè)永不銷毀的線程(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í)行的任務(wù)
- (void)test
{
    NSLog(@"%s %@", __func__, [NSThread currentThread]);
}

//點(diǎn)擊停止按鈕
- (IBAction)stop {
    // 在子線程調(diào)用CFRunLoopStop
    [self performSelector:@selector(stopThread) onThread:self.thread withObject:nil waitUntilDone:NO];
}

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

- (void)dealloc
{
    NSLog(@"%s", __func__);
    
    //就算把thread清空也不行,因?yàn)槿蝿?wù)還沒(méi)結(jié)束,線程就不會(huì)死。
    //self.thread = nil;
}
@end

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

運(yùn)行代碼,進(jìn)入界面,打印:

<MJThread: 0x60000233af00>{number = 3, name = (null)}----begin----

說(shuō)明線程開(kāi)始工作了。

點(diǎn)擊空白,打印:

-[ViewController test] <MJThread: 0x60000233af00>{number = 3, name = (null)}

說(shuō)明RunLoop接收到事件,開(kāi)始處理事件。

點(diǎn)擊stop打印:

-[ViewController stopThread] <MJThread: 0x6000015f2500>{number = 3, name = (null)}
<MJThread: 0x6000015f2500>{number = 3, name = (null)}----end----

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

點(diǎn)擊stop之后再退出當(dāng)前VC,打印:

-[ViewController dealloc]
-[MJThread dealloc]

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

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

壞內(nèi)存訪問(wèn)

那到底是誰(shuí)壞了呢?稍微想一下也知道是self控制器壞了。
其實(shí)原因就是[self performSelector:@selector(stopThread) onThread:self.thread withObject:nil waitUntilDone:NO]的最后一個(gè)參數(shù),當(dāng)傳入NO的時(shí)候,代表不等子線程(self.thread)里面的東西執(zhí)行完,主線程的dealloc方法會(huì)接著往下走,往下走ViewController的dealloc方法就執(zhí)行完了,self就不在了。
這時(shí)候子線程(self.thread)繼續(xù)做事,先拿到self對(duì)象和stopThread消息,然后在子線程給self對(duì)象發(fā)送topThread消息(內(nèi)部就是通過(guò)子線程的RunLoop循環(huán)),這時(shí)候self都不在了,拿不到了,所以在子線程的RunLoop循環(huán)里會(huì)報(bào)錯(cuò)壞內(nèi)存訪問(wèn)。

現(xiàn)在你應(yīng)該明白為什么會(huì)在RunLoop那行代碼報(bào)壞內(nèi)存訪問(wèn)錯(cuò)誤了吧!

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

運(yùn)行代碼,直接返回當(dāng)前VC,打印:

-[ViewController dealloc]
-[ViewController stopThread] <MJThread: 0x600000dda7c0>{number = 3, name = (null)}

可以發(fā)現(xiàn)控制器被銷毀了,CFRunLoopStop也調(diào)用了,但是線程還沒(méi)死,又活了,這就奇怪了。

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

while (!weakSelf.isStoped) {
    //beforeDat:過(guò)期時(shí)間,傳入distantFuture遙遠(yuǎn)的未來(lái),就是永遠(yuǎn)不會(huì)過(guò)期
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}

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

解決辦法:

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

再次運(yùn)行項(xiàng)目,點(diǎn)擊暫停,返回當(dāng)前VC,這時(shí)候又崩了:

崩了

點(diǎn)擊暫停之后RunLoop肯定停掉了,RunLoop停掉后,這時(shí)候的線程就不能用了,但是這時(shí)候thread還沒(méi)銷毀(還沒(méi)調(diào)用dealloc),因?yàn)閠hread還被self引用著,這時(shí)候訪問(wèn)一個(gè)不能用的thread就會(huì)報(bào)壞內(nèi)存訪問(wèn)錯(cuò)誤。

解決辦法也很簡(jiǎn)單,暫停RunLoop后把thread指針置為nil,并且如果發(fā)現(xiàn)子線程為nil就不在子線程做事情了。

代碼如下:

#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]);
    }];
    [self.thread start];
}

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

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

- (IBAction)stop {
    if (!self.thread) return;
    
    // 在子線程調(diào)用stop(waitUntilDone設(shè)置為YES,代表子線程的代碼執(zhí)行完畢后,這個(gè)方法才會(huì)往下走)
    [self performSelector:@selector(stopThread) onThread:self.thread withObject:nil waitUntilDone:YES];
}

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

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

二. 線程保活的封裝

上面的代碼雖然實(shí)現(xiàn)了線程保活并且也沒(méi)啥bug,但是用起來(lái)比較麻煩,下面就封裝一個(gè)可控制線程生命周期的類。

MJPermenantThread.h文件

#import <Foundation/Foundation.h>

//任務(wù)的回調(diào)
typedef void (^MJPermenantThreadTask)(void);

@interface MJPermenantThread : NSObject

/**
 開(kāi)啟線程
 */
//- (void)run;

/**
 在當(dāng)前子線程執(zhí)行一個(gè)任務(wù)
 */
- (void)executeTask:(MJPermenantThreadTask)task;

/**
 結(jié)束線程
 */
- (void)stop;

@end

MJPermenantThread.m文件

#import "MJPermenantThread.h"

/** MJThread **/
@interface MJThread : NSThread
@end
@implementation MJThread
- (void)dealloc
{
    NSLog(@"%s", __func__);
}
@end

/** MJPermenantThread **/
@interface MJPermenantThread()
@property (strong, nonatomic) MJThread *innerThread;
@property (assign, nonatomic, getter=isStopped) BOOL stopped;
@end

@implementation MJPermenantThread
#pragma mark - public methods
- (instancetype)init
{
    if (self = [super init]) {
        self.stopped = NO;
        
        __weak typeof(self) weakSelf = self;
        
        self.innerThread = [[MJThread alloc] initWithBlock:^{
            [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
            
            while (weakSelf && !weakSelf.isStopped) {
                [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
            }
        }];
        
        //自動(dòng)開(kāi)始線程
        [self.innerThread start];
    }
    return self;
}

//- (void)run
//{
//    if (!self.innerThread) return;
//
//    [self.innerThread start];
//}

- (void)executeTask:(MJPermenantThreadTask)task
{
    if (!self.innerThread || !task) return;
    
    [self performSelector:@selector(__executeTask:) onThread:self.innerThread withObject:task waitUntilDone:NO];
}

- (void)stop
{
    if (!self.innerThread) return;
    
    [self performSelector:@selector(__stop) onThread:self.innerThread withObject:nil waitUntilDone:YES];
}

//當(dāng)前對(duì)象死了,讓當(dāng)前對(duì)象里面的線程也死
- (void)dealloc
{
    NSLog(@"%s", __func__);
    
    [self stop];
}

#pragma mark - private methods
- (void)__stop
{
    self.stopped = YES;
    CFRunLoopStop(CFRunLoopGetCurrent());
    self.innerThread = nil;
}

- (void)__executeTask:(MJPermenantThreadTask)task
{
    task();
}

@end

在ViewController里面執(zhí)行以下代碼:

#import "ViewController.h"
#import "MJPermenantThread.h"

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

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.thread = [[MJPermenantThread alloc] init];
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    [self.thread executeTask:^{
        NSLog(@"執(zhí)行任務(wù) - %@", [NSThread currentThread]);
    }];
}

- (IBAction)stop {
    [self.thread stop];
}

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

@end

點(diǎn)擊跳轉(zhuǎn),跳轉(zhuǎn)到另外一個(gè)界面,在另外一個(gè)界面會(huì)自動(dòng)開(kāi)啟子線程,返回界面,發(fā)現(xiàn)界面和子線程會(huì)自動(dòng)銷毀。

小問(wèn)題:

保住線程的命為什么要用RunLoop,用強(qiáng)指針不就好了么?

準(zhǔn)確來(lái)講,使用RunLoop是為了讓線程保持激活狀態(tài),雖然用強(qiáng)指針指著它,可以保住線程的命,線程不會(huì)調(diào)用dealloc,這時(shí)候線程還在內(nèi)存中,但是線程的任務(wù)一旦執(zhí)行完畢,生命周期就結(jié)束,無(wú)法再使用,已經(jīng)是個(gè)廢物了。所以用強(qiáng)指針保住命沒(méi)什么意義,只能用RunLoop讓線程一直有事可做,一直保持激活狀態(tài)。

三. 線程保活的封裝(C語(yǔ)言)

MJPermenantThread.h文件

#import <Foundation/Foundation.h>

typedef void (^MJPermenantThreadTask)(void);

@interface MJPermenantThread : NSObject

/**
 開(kāi)啟線程
 */
//- (void)run;

/**
 在當(dāng)前子線程執(zhí)行一個(gè)任務(wù)
 */
- (void)executeTask:(MJPermenantThreadTask)task;

/**
 結(jié)束線程
 */
- (void)stop;

@end

MJPermenantThread.m文件

#import "MJPermenantThread.h"

/** MJThread **/
@interface MJThread : NSThread
@end
@implementation MJThread
- (void)dealloc
{
    NSLog(@"%s", __func__);
}
@end

/** MJPermenantThread **/
@interface MJPermenantThread()
@property (strong, nonatomic) MJThread *innerThread;
@end

@implementation MJPermenantThread
#pragma mark - public methods
- (instancetype)init
{
    if (self = [super init]) {
        self.innerThread = [[MJThread alloc] initWithBlock:^{
            NSLog(@"begin----");
            
            // 創(chuàng)建上下文(要初始化一下結(jié)構(gòu)體,否則結(jié)構(gòu)體里面有可能是垃圾數(shù)據(jù))
            CFRunLoopSourceContext context = {0};
            
            // 創(chuàng)建source
            CFRunLoopSourceRef source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
            
            // 往Runloop中添加source
            CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
            
            // 銷毀source
            CFRelease(source);
            
            // 啟動(dòng)
            //參數(shù):模式,過(guò)時(shí)時(shí)間(1.0e10一個(gè)很大的值),是否執(zhí)行完source后就會(huì)退出當(dāng)前l(fā)oop
            CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e10, false);
            
            //如果使用的是C語(yǔ)言的方式就可以通過(guò)最后一個(gè)參數(shù)讓執(zhí)行完source之后不退出當(dāng)前Loop,所以就可以不用stopped屬性了
//            while (weakSelf && !weakSelf.isStopped) {
//                // 第3個(gè)參數(shù):returnAfterSourceHandled,設(shè)置為true,代表執(zhí)行完source后就會(huì)退出當(dāng)前l(fā)oop
//                CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e10, true);
//            }
            
            NSLog(@"end----");
        }];
        
        [self.innerThread start];
    }
    return self;
}

//- (void)run
//{
//    if (!self.innerThread) return;
//
//    [self.innerThread start];
//}

- (void)executeTask:(MJPermenantThreadTask)task
{
    if (!self.innerThread || !task) return;
    
    [self performSelector:@selector(__executeTask:) onThread:self.innerThread withObject:task waitUntilDone:NO];
}

- (void)stop
{
    if (!self.innerThread) return;
    
    [self performSelector:@selector(__stop) onThread:self.innerThread withObject:nil waitUntilDone:YES];
}

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

#pragma mark - private methods
- (void)__stop
{
    CFRunLoopStop(CFRunLoopGetCurrent());
    self.innerThread = nil;
}

- (void)__executeTask:(MJPermenantThreadTask)task
{
    task();
}

@end

C語(yǔ)言方式和OC方式達(dá)到的效果都是一樣的,但是C語(yǔ)言方式控制的更精準(zhǔn),可以控制執(zhí)行完source后不退出當(dāng)前l(fā)oop,這樣就不用寫(xiě)while循環(huán)了。

Demo地址:線程保活

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容