Runloop

1.load方法下邊為什么建議加上dispatch-once?
雖然load方法只調(diào)用一次,可能某些情況下回顯示調(diào)用load,這樣dispatch-once里的代碼也會(huì)執(zhí)行一次。
bt = breakpoint trace

2.Runloop與線程
Runloop保存在一個(gè)全局的Dictionary里,線程做為key,Runloop作為value;
Runloop會(huì)在線程結(jié)束時(shí)銷(xiāo)毀。

3.runloop數(shù)據(jù)結(jié)構(gòu)是什么樣子的?
struct __CFRunLoop {
    pthread_t _pthread;
    uint32_t _winthread;
    CFMutableSetRef _commonModes;
    CFMutableSetRef _commonModeItems; //放了_commonModes運(yùn)行的timer等
    CFRunLoopModeRef _currentMode;
    CFMutableSetRef _modes;
};

typedef struct __CFRunLoopMode *CFRunLoopModeRef;
struct __CFRunLoopMode {
    CFStringRef _name;
    CFMutableSetRef _sources0;
    CFMutableSetRef _sources1;
    CFMutableArrayRef _observers;
    CFMutableArrayRef _timers;

};

4.源碼分析runloop執(zhí)行流程:
touchbegan中斷點(diǎn)bt一下找到CFRunLoopRunSpecific,然后__CFRunLoopRun:

static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
    int32_t retVal = 0;
    do {
        //通知Observers:即將處理Timers
        if (rlm->_observerMask & kCFRunLoopBeforeTimers) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
        //通知Observers:即將處理Sources
        if (rlm->_observerMask & kCFRunLoopBeforeSources) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
        //處理blocks
    __CFRunLoopDoBlocks(rl, rlm);
        //處理source0
        Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
        if (sourceHandledThisLoop) {
        //處理blocks
            __CFRunLoopDoBlocks(rl, rlm);
    }
        //判斷有誤source1
        if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
            //如果有source1,就跳轉(zhuǎn)到handle_msg
            goto handle_msg;
        }
        //通知Observer即將休眠
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
        __CFRunLoopSetSleeping(rl);
        //等待別的消息來(lái)喚醒當(dāng)前線程
        __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);

        __CFRunLoopUnsetSleeping(rl);
        //通知Observer結(jié)束休眠
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);

   handle_msg:;

        if (被timer喚醒) {
            //處理timers
            __CFRunLoopDoTimers(rl, rlm, mach_absolute_time());
        }
        else if (被GCD喚醒) {
            //處理gcd
            __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
        } else {//被source1喚醒
            //處理source1
            __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply);
        }
        //處理blocks
        __CFRunLoopDoBlocks(rl, rlm);
        
        //設(shè)置返回值
    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;
    }
        voucher_mach_msg_revert(voucherState);
        os_release(voucherCopy);

    } while (0 == retVal);
    return retVal;
}

5.  阻塞是切換到內(nèi)核態(tài),沒(méi)有消息就讓線程休眠,有消息就喚醒線程。
runloop實(shí)現(xiàn)原理:
用戶(hù)態(tài):處理消息。
內(nèi)核態(tài):沒(méi)有消息就讓線程休眠,有消息就喚醒線程。

6.runloop是怎么響應(yīng)用戶(hù)操作的,具體流程是什么樣的?source0->source1
source0去捕捉事件,source1處理事件。

7.平時(shí)都是怎么用runloop的?
(1)控制線程生命周期
線程保活OC實(shí)現(xiàn):

@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):
封裝實(shí)現(xiàn)

上面2個(gè)封裝的使用前提條件:在某個(gè)固定子線程下串行執(zhí)行某個(gè)任務(wù),非并發(fā)的。

(2)解決NSTimer在滑動(dòng)時(shí)停止工作的問(wèn)題
使用手動(dòng)定時(shí)器把它加到commonmode下即可。

(3)監(jiān)控應(yīng)用卡頓

(4)性能優(yōu)化










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

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

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