IOS底層原理 -6.runloop

1. 簡介

iOS 中負責程序運行循環,在程序運行過程中循環做一些事情;保持程序的運行,程序運行時會在main函數中創建一個runloop,負責主線程的持續運行;及處理app中的各種事件的響應,例如:NSTimer,UITouch等;在程序需要處理事件時候runloop就喚起線程,當不許處理事件的時候線程進入休眠狀態,這樣可以節省線程資源。
在OC中每一個線程都有唯一的一個Runloop對象;Runloop對象保存在一個全局的dictionary裏,線程作爲鍵Runloop作爲值;線程剛創建的時候是沒有runloop,當第一次獲取的時候創建;mian線程的Runloop程序運行時已經獲取了。

2. Runloop 對象獲取

源碼可以在蘋果開源網站下載,
2.1 get方法
在源碼裏找到CFRunLoop.c文件,在該文件裏搜索 CFRunloopGetCurrent()方法;該方法裏調用了_CFRunLoopGet0(pthread_self());,下面看下該方法實現:

CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
    if (pthread_equal(t, kNilPthreadT)) {
	t = pthread_main_thread_np();
    }
    __CFLock(&loopsLock);
    if (!__CFRunLoops) {//如果不存在,則創建一個保存Runloop對象的字典,
        __CFUnlock(&loopsLock);
	CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
	CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());//創建線程的runloop
	CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
	if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
	    CFRelease(dict);
	}
	CFRelease(mainLoop);
        __CFLock(&loopsLock);
    }
    CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));//從字典裏取值
    __CFUnlock(&loopsLock);
    if (!loop) {//如果沒有獲取到
	CFRunLoopRef newLoop = __CFRunLoopCreate(t);//則創建
        __CFLock(&loopsLock);
	loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
	if (!loop) {
	    CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
	    loop = newLoop;
	}
        // don't release run loops inside the loopsLock, because CFRunLoopDeallocate may end up taking it
        __CFUnlock(&loopsLock);
	CFRelease(newLoop);
    }
    if (pthread_equal(t, pthread_self())) {
        _CFSetTSD(__CFTSDKeyRunLoop, (void *)loop, NULL);
        if (0 == _CFGetTSD(__CFTSDKeyRunLoopCntr)) {
            _CFSetTSD(__CFTSDKeyRunLoopCntr, (void *)(PTHREAD_DESTRUCTOR_ITERATIONS-1), (void (*)(void *))__CFFinalizeRunLoop);
        }
    }
    return loop;
}

2.2 runLoop 相關類及執行狀態
常用相關的類主要有5個:CFRunLoopRef、CFRunLoopModelRef、CFRunLoopSourceRef、CFRunLoopTimerRef、CFRunLoopObseverRef;
CFRunLoop在公開的源碼裏的實現是:

struct __CFRunLoop {
    CFRuntimeBase _base;
    pthread_t _pthread;
    uint32_t _winthread;
    CFMutableSetRef _commonModes;
    CFMutableSetRef _commonModeItems;
    CFRunLoopModeRef _currentMode;//當前的模式
    CFMutableSetRef _modes;//所有的運行模式
};
struct __CFRunLoopMode {
    CFRuntimeBase _base;
    CFStringRef _name;
    CFMutableSetRef _sources0;//觸摸事件,performSelector:onThread:withobject:
    CFMutableSetRef _sources1;
    CFMutableArrayRef _observers;
    CFMutableArrayRef _timers;
    CFMutableDictionaryRef _portToV1SourceMap;
};

從上述源碼中,可以看出一個RunLooop可以包含多個mode,每一個mode又包含多個_sources0、_sources1、_observers、_timers;當前是那個mode取決於_currentMode,同時也只能存在一種模式可以作爲_currentMode,當需要切換到另外一種模式的時候會退出當前的mode;常用的有兩種mode:kCFRunLoopDefaultMode 主線程一般都是運行在該mode下、UITrackingRunLoopMode 界面跟蹤mode,界面滑動觸摸,保證界面滑動時候不受其他mode的影響;
看一下觸摸事件的函數調用堆棧:
在這裏插入圖片描述
接下來通過一段代碼驗證一下模式的切換:

  CFRunLoopObserverRef obRef = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities,YES,0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
       
        switch (activity) {
            case kCFRunLoopEntry:{
                CFRunLoopMode mode =  CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
                NSLog(@"kCFRunLoopEntry %@",mode);
                CFRelease(mode);
            }
    
                break;
            case kCFRunLoopBeforeTimers: {
                
                break;
            }
            case kCFRunLoopBeforeSources: {
                
                break;
            }
            case kCFRunLoopBeforeWaiting: {
                
                break;
            }
            case kCFRunLoopAfterWaiting: {
                
                break;
            }
            case kCFRunLoopExit: {
                CFRunLoopMode mode =  CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
                NSLog(@"kCFRunLoopExit %@",mode);
                CFRelease(mode);
                break;
            }
            case kCFRunLoopAllActivities: {
                
                break;
            }
        }
    });
//  CFRunLoopObserverRef obRef =  CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){
//
//    }, NULL);
    CFRunLoopAddObserver(CFRunLoopGetCurrent(), obRef, kCFRunLoopCommonModes);
    CFRelease(obRef);

滑動view後的輸出:
在這裏插入圖片描述

3. RunLoop 運行邏輯

在這裏插入圖片描述
以下部分內容來自iOS底層原理總結 - RunLoop

3.1 RunLoop 調用過程

RunLoop 在CoreFoundation框架中可以添加消息監聽

    CFRunLoopObserverRef obRef = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities,YES,0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
       
        switch (activity) {
            case kCFRunLoopEntry:{
                CFRunLoopMode mode =  CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
                NSLog(@"kCFRunLoopEntry 進入 %@",mode);
                CFRelease(mode);
            }
    
                break;
            case kCFRunLoopBeforeTimers: {
                CFRunLoopMode mode =  CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
                NSLog(@"kCFRunLoopBeforeTimers 處理timer %@",mode);
                CFRelease(mode);
                break;
            }
            case kCFRunLoopBeforeSources: {
                CFRunLoopMode mode =  CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
                NSLog(@"kCFRunLoopBeforeSources 處理source %@",mode);
                CFRelease(mode);
                break;
            }
            case kCFRunLoopBeforeWaiting: {
                CFRunLoopMode mode =  CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
                NSLog(@"kCFRunLoopBeforeWaiting 開始進入休息 %@",mode);
                CFRelease(mode);
                break;
            }
            case kCFRunLoopAfterWaiting: {
                CFRunLoopMode mode =  CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
                NSLog(@"kCFRunLoopAfterWaiting 已經恢復 %@",mode);
                CFRelease(mode);
                break;
            }
            case kCFRunLoopExit: {
                CFRunLoopMode mode =  CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
                NSLog(@"kCFRunLoopExit 退出了 %@",mode);
                CFRelease(mode);
                break;
            }
            case kCFRunLoopAllActivities: {
                CFRunLoopMode mode =  CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
                NSLog(@"kCFRunLoopAllActivities %@",mode);
                CFRelease(mode);
                break;
            }
        }
    });
//  CFRunLoopObserverRef obRef =  CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){
//
//    }, NULL);
    CFRunLoopAddObserver(CFRunLoopGetCurrent(), obRef, kCFRunLoopCommonModes);
    CFRelease(obRef);

控制檯輸出,部分代碼精簡:

2019-09-25 09:06:58.969802+0800 testRunLoop[5136:43000] kCFRunLoopBeforeTimers 處理timer kCFRunLoopDefaultMode
2019-09-25 09:06:58.970439+0800 testRunLoop[5136:43000] kCFRunLoopBeforeSources 處理source kCFRunLoopDefaultMode
2019-09-25 09:06:58.970643+0800 testRunLoop[5136:43000] kCFRunLoopBeforeTimers 處理timer kCFRunLoopDefaultMode
2019-09-25 09:06:58.970760+0800 testRunLoop[5136:43000] kCFRunLoopBeforeSources 處理source kCFRunLoopDefaultMode
2019-09-25 09:06:58.970909+0800 testRunLoop[5136:43000] kCFRunLoopBeforeTimers 處理timer kCFRunLoopDefaultMode
2019-09-25 09:09:00.007058+0800 testRunLoop[5136:43000] kCFRunLoopBeforeWaiting 開始進入休息 kCFRunLoopDefaultMode
2019-09-25 09:10:00.003054+0800 testRunLoop[5136:43000] kCFRunLoopAfterWaiting 已經恢復 kCFRunLoopDefaultMode
2019-09-25 09:10:00.006223+0800 testRunLoop[5136:43000] kCFRunLoopBeforeTimers 處理timer kCFRunLoopDefaultMode
2019-09-25 09:10:00.006520+0800 testRunLoop[5136:43000] kCFRunLoopBeforeSources 處理source kCFRunLoopDefaultMode
2019-09-25 09:10:00.006804+0800 testRunLoop[5136:43000] kCFRunLoopBeforeWaiting 開始進入休息 kCFRunLoopDefaultMode
2019-09-25 09:11:00.002788+0800 testRunLoop[5136:43000] kCFRunLoopAfterWaiting 已經恢復 kCFRunLoopDefaultMode
2019-09-25 09:11:00.005685+0800 testRunLoop[5136:43000] kCFRunLoopBeforeTimers 處理timer kCFRunLoopDefaultMode
2019-09-25 09:11:00.005937+0800 testRunLoop[5136:43000] kCFRunLoopBeforeSources 處理source kCFRunLoopDefaultMode
2019-09-25 09:11:00.006132+0800 testRunLoop[5136:43000] kCFRunLoopBeforeWaiting 開始進入休息 kCFRunLoopDefaultMode

3.2 源碼簡介

// 共外部調用的公開的CFRunLoopRun方法,其內部會調用CFRunLoopRunSpecific
void CFRunLoopRun(void) {   /* DOES CALLOUT */
    int32_t result;
    do {
        result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
        CHECK_FOR_FORK();
    } while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}

// 經過精簡的 CFRunLoopRunSpecific 函數代碼,其內部會調用__CFRunLoopRun函數
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {     /* DOES CALLOUT */

    // 通知Observers : 進入Loop
    // __CFRunLoopDoObservers內部會調用 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__
函數
    if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
    
    // 核心的Loop邏輯
    result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
    
    // 通知Observers : 退出Loop
    if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);

    return result;
}

// 精簡後的 __CFRunLoopRun函數,保留了主要代碼
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
    int32_t retVal = 0;
    do {
        // 通知Observers:即將處理Timers
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers); 
        
        // 通知Observers:即將處理Sources
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
        
        // 處理Blocks
        __CFRunLoopDoBlocks(rl, rlm);
        
        // 處理Sources0
        if (__CFRunLoopDoSources0(rl, rlm, stopAfterHandle)) {
            // 處理Blocks
            __CFRunLoopDoBlocks(rl, rlm);
        }
        
        // 如果有Sources1,就跳轉到handle_msg標記處
        if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
            goto handle_msg;
        }
        
        // 通知Observers:即將休眠
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
        
        // 進入休眠,等待其他消息喚醒
        __CFRunLoopSetSleeping(rl);
        __CFPortSetInsert(dispatchPort, waitSet);
        do {
            __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
        } while (1);
        
        // 醒來
        __CFPortSetRemove(dispatchPort, waitSet);
        __CFRunLoopUnsetSleeping(rl);
        
        // 通知Observers:已經喚醒
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
        
    handle_msg: // 看看是誰喚醒了RunLoop,進行相應的處理
        if (被Timer喚醒的) {
            // 處理Timer
            __CFRunLoopDoTimers(rl, rlm, mach_absolute_time());
        }
        else if (被GCD喚醒的) {
            __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
        } else { // 被Sources1喚醒的
            __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply);
        }
        
        // 執行Blocks
        __CFRunLoopDoBlocks(rl, rlm);
        
        // 根據之前的執行結果,來決定怎麼做,爲retVal賦相應的值
        if (sourceHandledThisLoop && stopAfterHandle) {
            retVal = kCFRunLoopRunHandledSource;
        } else if (timeout_context->termTSR < mach_absolute_time()) {
            retVal = kCFRunLoopRunTimedOut;
        } else if (__CFRunLoopIsStopped(rl)) {
            __CFRunLoopUnsetStopped(rl);
            retVal = kCFRunLoopRunStopped;
        } else if (rlm->_stopped) {
            rlm->_stopped = false;
            retVal = kCFRunLoopRunStopped;
        } else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) {
            retVal = kCFRunLoopRunFinished;
        }
        
    } while (0 == retVal);
    
    return retVal;
}

3.3 對timer的影響

    static int timerCount = 0;
    NSTimer *timer = [NSTimer timerWithTimeInterval:1.0f repeats:YES block:^(NSTimer * _Nonnull timer) {
//        NSLog(@"timerCount = %d",self->_timerCount++);
        NSLog(@"timerCount = %d",timerCount++);
    }];
    //NSDefaultRunLoopMode kCFRunLoopCommonModes
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];

在這裏插入圖片描述
從控制檯的時間戳裏可以看出,屏幕滑動會對timer的影響;將NSDefaultRunLoopMode換成kCFRunLoopCommonModes屏幕的滑動就不會對timer產生影響;

4 子線程中對NSTimer的影響

4.1 子線程如何使用NSTimer,通過上面的分析知道:主線程runLoop成程序啓動後會運行,而子線程的runLoop不主動調用是沒有的,而且即使主動調用子線程的runloop是默認不運行的,因此子線程的timer運行,一般我們按照如下方式處理:

#import "TestTimer.h"

@interface TestTimer ()
@property(nonatomic,strong)NSTimer *timer;
@property(nonatomic,strong)dispatch_queue_t que;
@end

@implementation TestTimer
-(void)run{
    static int timerCount = 0;
    self.que = dispatch_queue_create("ddddd", DISPATCH_QUEUE_CONCURRENT);
    __weak typeof(self) weakSelf = self;
    dispatch_async(_que, ^{
         __weak typeof(weakSelf) strongSelf = weakSelf;
        CFRunLoopObserverRef obRef = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities,YES,0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
            
            switch (activity) {
                case kCFRunLoopEntry:{
                    CFRunLoopMode mode =  CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
                    NSLog(@"kCFRunLoopEntry 進入 %@",mode);
                    CFRelease(mode);
                }
                    
                    break;
                case kCFRunLoopBeforeTimers: {
                    //                CFRunLoopMode mode =  CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
                    //                NSLog(@"kCFRunLoopBeforeTimers 處理timer %@",mode);
                    //                CFRelease(mode);
                    break;
                }
                case kCFRunLoopBeforeSources: {
                    //                CFRunLoopMode mode =  CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
                    //                NSLog(@"kCFRunLoopBeforeSources 處理source %@",mode);
                    //                CFRelease(mode);
                    break;
                }
                case kCFRunLoopBeforeWaiting: {
                    //                CFRunLoopMode mode =  CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
                    //                NSLog(@"kCFRunLoopBeforeWaiting 開始進入休息 %@",mode);
                    //                CFRelease(mode);
                    break;
                }
                case kCFRunLoopAfterWaiting: {
                    //                CFRunLoopMode mode =  CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
                    //                NSLog(@"kCFRunLoopAfterWaiting 已經恢復 %@",mode);
                    //                CFRelease(mode);
                    break;
                }
                case kCFRunLoopExit: {
                    CFRunLoopMode mode =  CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
                    NSLog(@"kCFRunLoopExit 退出了 %@",mode);
                    CFRelease(mode);
                    break;
                }
                case kCFRunLoopAllActivities: {
                    CFRunLoopMode mode =  CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
                    NSLog(@"kCFRunLoopAllActivities %@",mode);
                    CFRelease(mode);
                    break;
                }
            }
        });
        CFRunLoopAddObserver(CFRunLoopGetCurrent(), obRef, kCFRunLoopCommonModes);
        CFRelease(obRef);
        strongSelf.timer = [NSTimer timerWithTimeInterval:1.0f repeats:YES block:^(NSTimer * _Nonnull timer) {
            //        NSLog(@"timerCount = %d",self->_timerCount++);
            NSLog(@"timerCount = %d",timerCount++);
        }];
        //NSDefaultRunLoopMode UITrackingRunLoopMode NSRunLoopCommonModes
        [[NSRunLoop currentRunLoop] addTimer:strongSelf.timer forMode:NSRunLoopCommonModes];
        [[NSRunLoop currentRunLoop] run];        
    });
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [self.timer invalidate];
    });
}
-(void)dealloc{
    NSLog(@"%s",__func__);
}
@end

運行後輸出:

2019-09-25 10:24:05.161121+0800 testRunLoop[19144:159757] kCFRunLoopEntry 進入 kCFRunLoopDefaultMode
2019-09-25 10:24:06.162717+0800 testRunLoop[19144:159757] timerCount = 0
//...省略
2019-09-25 10:24:14.162738+0800 testRunLoop[19144:159757] timerCount = 8
2019-09-25 10:24:15.161226+0800 testRunLoop[19144:159625] -[TestTimer dealloc]

從上面的運行結果發現,runloop在定時器結束後並沒有退出循環,如果想在結束後結束子線程循環則需要調用:[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];

2019-09-25 10:28:39.161729+0800 testRunLoop[19809:166720] kCFRunLoopEntry 進入 kCFRunLoopDefaultMode
2019-09-25 10:28:40.165492+0800 testRunLoop[19809:166720] timerCount = 0
//...省略
2019-09-25 10:28:49.162015+0800 testRunLoop[19809:166720] timerCount = 9
2019-09-25 10:28:49.162109+0800 testRunLoop[19809:166667] -[TestTimer dealloc]
2019-09-25 10:28:49.162376+0800 testRunLoop[19809:166720] kCFRunLoopExit 退出了 kCFRunLoopDefaultMode

4. 線程保活

注意:NSRunloop的run方法是無法停止的,一旦掉用該方法就等於開啓一個永遠不會銷燬的線程;及即使調用CFRunloopStop()也只會是停止一次的run;及調用run會無限開啓run方法;
看一下實現:

#import "LYMPermemantThread.h"

@interface LYMThread : NSThread

@end

@implementation LYMThread
-(void)dealloc{
    NSLog(@"%s",__func__);
}
@end
@interface LYMPermemantThread ()
@property(nonatomic,strong)LYMThread *innerThred;
@property(nonatomic,assign,getter=isStopped)BOOL stopped;
//@property(nonatomic,copy)void(^innerHandler)(void);
@end

@implementation LYMPermemantThread
- (instancetype)init{
    if (self = [super init]) {
        self.stopped = NO;
        __weak typeof(self) weakSelf = self;
        self.innerThred = [[LYMThread alloc]initWithBlock:^{
            [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
            
            while (weakSelf && !weakSelf.isStopped) {//一次循環後會退出,這裏爲防止退出,加入循環在需要退出的時候退出線程
                [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
            }
            
        }];
        [self.innerThred start];
    }
    return self;
}
- (void)run{
//    if (!_innerThred) {
//        return;
//    }
//    [self.innerThred start];
}
- (void)execTaskWithHandler:(void(^)(void))handler{
    if (!_innerThred || !handler) {
        return;
    }
    [self performSelector:@selector(__execBcock:) onThread:self.innerThred withObject:handler waitUntilDone:NO];
}
- (void)stop{
    if (!_innerThred) {
        return;
    }
    [self performSelector:@selector(__stop) onThread:self.innerThred withObject:nil waitUntilDone:YES];
}

- (void) __stop{
    self.stopped = YES;
    CFRunLoopStop(CFRunLoopGetCurrent());
    self.innerThred = nil;
}
- (void)__execBcock:(void(^)(void))handler{
    if (handler) {
        handler();
    }
}
-(void)dealloc{
    NSLog(@"%s",__func__);
    [self stop];
}
@end

使用CF框架實現:

- (instancetype)init{
    if (self = [super init]) {
        self.stopped = NO;
        __weak typeof(self) weakSelf = self;
        self.innerThred = [[LYMThread alloc]initWithBlock:^{
//            [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
//
//            while (weakSelf && !weakSelf.isStopped) {
//                [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
//            }
            CFRunLoopSourceContext cotext = {0};
            CFRunLoopSourceRef source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &cotext);
            CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
            CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e10, false);//false 標識source結束後不退出runloop
            
        }];
        [self.innerThred start];
    }
    return self;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章