RunLoop底层原理探究

RunLoop 初探

Runloop与线程的关系是一一对应的,一个线程一次只能执行一个任务,任务执行完毕后线程就会被销毁,而Runloop的作用就是来管理和调度线程是他在没有任务的时候不会被销毁。

对于主线程来说,runloop在程序一启动就默认创建好了。
对于子线程来说,runloop是懒加载的,只有当我们使用的时候才会创建

RunLoop运行模式(一共有5种)
Default NSDefaultRunLoopMode (Cocoa) kCFRunLoopDefaultMode(Core Foundation)
Event tracking NSEventTrackingRunLoopMode (Cocoa)
Common modes NSRunLoopCommonModes (Cocoa) kCFRunLoopCommonModes(Core Foundation)

Connection NSConnectionReplyMode (Cocoa)
Modal NSModalPanelRunLoopMode (Cocoa)

5种RunLoop

  1. CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION(调用timer,performselector)

  2. CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE(GCD主队列)

  3. FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK(响应Block)

  4. CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION(observer)

  5. CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION

  6. CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION

RunLoop与线程的关系

主线程runloop的mode为Default,Event tracking 是触摸事件产生的时候,Common modes 是占位符相当于 NSDefaultRunLoopMode + UITrackingRunLoopMode 。一些定时器的触摸失效就是因为可能被添加到了默认的Runloop里面而不是common modes。子线程如果不保证任务执行完不被销毁也一定要创建一个Runloop来保证线程的存活

RunLoop是一个消息处理机制,系统交给它进行处理各种信息 一个简单程序执行的过程是 运行->处理计算->完成 -> 结束任务

UIApplication Main的函数注释是 如果为PrincipalClassName指定了nil,则使用info.plist中nsPrincipalClass的值。如果没有指定了nsPrincipalClass键,使用了uiApplication类。委托类将使用init进行实例化。所以就到了AppDelegate里面。一个App启动的流程

int main(int argc, char * argv[]) {
    @autoreleasepool {

        // If nil is specified for principalClassName, the value for NSPrincipalClass from the Info.plist is used. If there is no
        // NSPrincipalClass key specified, the UIApplication class is used. The delegate class will be instantiated using init.
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));;
    }
}
复制代码

#if DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_IPHONESIMULATOR
CF_EXPORT pthread_t _CF_pthread_main_thread_np(void);
#define pthread_main_thread_np() _CF_pthread_main_thread_np()
#endif
CFRunLoopRef CFRunLoopGetMain(void) {
    CHECK_FOR_FORK();
    static CFRunLoopRef __main = NULL; // no retain needed
    if (!__main) __main = _CFRunLoopGet0(pthread_main_thread_np()); // no CAS needed
    return __main;
}
复制代码

函数中传入了当前的主线程pthread_main_thread_np在宏定义中定义为当前主线程主线程进入点进_CFRunLoopGet0b

// should only be called by Foundation
// t==0 is a synonym for "main thread" that always works
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
   /// '判断当前是否是主线程'
    if (pthread_equal(t, kNilPthreadT)) {
    t = pthread_main_thread_np();
    }
    __CFSpinLock(&loopsLock);
    if (!__CFRunLoops) {
        __CFSpinUnlock(&loopsLock);
    /// '这里定义了  CFMutableDictionaryRef  创造主Runloop并且将其相关联 ///这里达'
    CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
    CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
    **CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);**
    if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
        CFRelease(dict);
    }
    CFRelease(mainLoop);
        __CFSpinLock(&loopsLock);
    }
///  '这里 直接把t指针取出来 拿到runloop 从这可以看出 线程和Runloop是一一对应的关系 key-- value'
    CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
    __CFSpinUnlock(&loopsLock);
    if (!loop) {
    CFRunLoopRef newLoop = __CFRunLoopCreate(t);
        __CFSpinLock(&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
        __CFSpinUnlock(&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;
}
复制代码

从上述代码可以看出来 线程和Runloop是一一对应的关系 key-- value 开启一条子线程里面执行一个定时器如果Runloop不开启的时候那么里面的计时器是不会执行的从这可以看出 子线程 Runloop默认不开启,需要手动开启

RunLoop 结构

Runloop是个结构体他是一个对象

struct __CFRunLoop {
    CFRuntimeBase _base;
    pthread_mutex_t _lock;          /* locked for accessing mode list */
    __CFPort _wakeUpPort;           // used for CFRunLoopWakeUp 
    Boolean _unused;
    volatile _per_run_data *_perRunData;              // reset for runs of the run loop
    pthread_t _pthread;
    uint32_t _winthread;
    CFMutableSetRef _commonModes; '/// models 集合'
    CFMutableSetRef _commonModeItems; '/// item 是个集合'
    CFRunLoopModeRef _currentMode;  '/// 当前models'
    CFMutableSetRef _modes; '/// models 集合'
    struct _block_item *_blocks_head;
    struct _block_item *_blocks_tail;
    CFTypeRef _counterpart;
};
复制代码

Model

RunLoop model 的结构

struct __CFRunLoopMode {
    CFRuntimeBase _base;
    pthread_mutex_t _lock;  /* must have the run loop locked before locking this */
    CFStringRef _name;
    Boolean _stopped;
    char _padding[3];
    CFMutableSetRef _sources0;' /// 集合'
    CFMutableSetRef _sources1; '/// 集合'
    CFMutableArrayRef _observers; '/// 数组'
    CFMutableArrayRef _timers; '/// 数组'
    CFMutableDictionaryRef _portToV1SourceMap;
    __CFPortSet _portSet;
    CFIndex _observerMask;
#if USE_DISPATCH_SOURCE_FOR_TIMERS
    dispatch_source_t _timerSource;
    dispatch_queue_t _queue;
    Boolean _timerFired; // set to true by the source when a timer has fired
    Boolean _dispatchTimerArmed;
#endif
#if USE_MK_TIMER_TOO
    mach_port_t _timerPort;
    Boolean _mkTimerArmed;
#endif
#if DEPLOYMENT_TARGET_WINDOWS
    DWORD _msgQMask;
    void (*_msgPump)(void);
#endif
    uint64_t _timerSoftDeadline; /* TSR */
    uint64_t _timerHardDeadline; /* TSR */
};
复制代码

RunLoop与model是一对多的关系,他在RunLoop里是以一个集合的形式存在的。Runloop只能在一个model下运行。但是可以拥有多个model。

timer

timer在Runloop的model里面是以数组的形式存在 源码里有个关键的方法是CFRunLoopRun调用这个方法最后会走到

CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) 这个方法里会有一个**__CFRunLoopDoBlocks**通过这个方法

static Boolean __CFRunLoopDoBlocks(CFRunLoopRef rl, CFRunLoopModeRef rlm) { // Call with rl and rlm locked
    if (!rl->_blocks_head) return false;
    if (!rlm || !rlm->_name) return false;
    Boolean did = false;
    struct _block_item *head = rl->_blocks_head;
    struct _block_item *tail = rl->_blocks_tail;
    rl->_blocks_head = NULL;
    rl->_blocks_tail = NULL;
    CFSetRef commonModes = rl->_commonModes;
    CFStringRef curMode = rlm->_name;
    __CFRunLoopModeUnlock(rlm);
    __CFRunLoopUnlock(rl);
    struct _block_item *prev = NULL;
    struct _block_item *item = head; "model里的item以链表的形式存在,只要item存在就会一直存在直到为空为止。"
    while (item) {
        struct _block_item *curr = item;
        item = item->_next; "访问链表下层,并为其赋值,"
        Boolean doit = false;
        if (CFStringGetTypeID() == CFGetTypeID(curr->_mode)) {
            "// timer 加入的mode 和 我们现在runloop的mode 相等
            //  或者 curr->_mode = kCFRunLoopCommonModes 相等
            // 事务就能执行 timer只要加入到当前model或者commanmodel里他都可以执行"
            doit = CFEqual(curr->_mode, curMode) || (CFEqual(curr->_mode, kCFRunLoopCommonModes) && CFSetContainsValue(commonModes, curMode));
        } else {
            doit = CFSetContainsValue((CFSetRef)curr->_mode, curMode) || (CFSetContainsValue((CFSetRef)curr->_mode, kCFRunLoopCommonModes) && CFSetContainsValue(commonModes, curMode));
        }
        if (!doit) prev = curr; "如果doit存在则执行响应的block"
        if (doit) { 
            if (prev) prev->_next = item;
            if (curr == head) head = item;
            if (curr == tail) tail = prev;
            void (^block)(void) = curr->_block;
            CFRelease(curr->_mode);
            free(curr);
            if (doit) {
                __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block);"block model的runloop"
                did = true;
            }
            Block_release(block); // do this before relocking to prevent deadlocks where some yahoo wants to run the run loop reentrantly from their dealloc
        }
    }
    __CFRunLoopLock(rl);
    __CFRunLoopModeLock(rlm);
    if (head) {
        tail->_next = rl->_blocks_head;
        rl->_blocks_head = head;
        if (!rl->_blocks_tail) rl->_blocks_tail = tail;
    }
    return did;
}
复制代码

之所以说是__CFRunLoopDoBlocks是因为最外层会有一个Runloop对一个timer进行添加那我们看下

RunLoop.current.addtimer()
复制代码

的底层实现

static void __CFRunLoopAddItemToCommonModes(const void *value, void *ctx) {
    CFStringRef modeName = (CFStringRef)value;
    CFRunLoopRef rl = (CFRunLoopRef)(((CFTypeRef *)ctx)[0]);
    CFTypeRef item = (CFTypeRef)(((CFTypeRef *)ctx)[1]);
    if (CFGetTypeID(item) == CFRunLoopSourceGetTypeID()) {
        CFRunLoopAddSource(rl, (CFRunLoopSourceRef)item, modeName);
    } else if (CFGetTypeID(item) == CFRunLoopObserverGetTypeID()) {
        CFRunLoopAddObserver(rl, (CFRunLoopObserverRef)item, modeName);
    } else if (CFGetTypeID(item) == CFRunLoopTimerGetTypeID()) {"item对应的timerid所以会走这个方法"
        CFRunLoopAddTimer(rl, (CFRunLoopTimerRef)item, modeName);
    }
}


"可以看到Timer的模式加入到了CFRunLoopAddTimer方法"
void CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef rlt, CFStringRef modeName) {
    CHECK_FOR_FORK();
    if (__CFRunLoopIsDeallocating(rl)) return;
    if (!__CFIsValid(rlt) || (NULL != rlt->_runLoop && rlt->_runLoop != rl)) return;
    __CFRunLoopLock(rl);
    if (modeName == kCFRunLoopCommonModes) { "如果model是commanmodes的话"
        CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL;
        if (NULL == rl->_commonModeItems) {
            rl->_commonModeItems = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
        }
        "将timer对象的model设置为commonmodels ,然后timer加到items里面去,"
        CFSetAddValue(rl->_commonModeItems, rlt); 
        if (NULL != set) {
            CFTypeRef context[2] = {rl, rlt};
            /* add new item to all common-modes */
            // timer -- items()
            CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context);
            CFRelease(set);
        }
    } else {
        CFRunLoopModeRef rlm = __CFRunLoopFindMode(rl, modeName, true);
        if (NULL != rlm) {
            if (NULL == rlm->_timers) {
                CFArrayCallBacks cb = kCFTypeArrayCallBacks;
                cb.equal = NULL;
                rlm->_timers = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &cb);
            }
        }
        if (NULL != rlm && !CFSetContainsValue(rlt->_rlModes, rlm->_name)) {
            __CFRunLoopTimerLock(rlt);
            if (NULL == rlt->_runLoop) {
                rlt->_runLoop = rl;
            } else if (rl != rlt->_runLoop) {
                __CFRunLoopTimerUnlock(rlt);
                __CFRunLoopModeUnlock(rlm);
                __CFRunLoopUnlock(rl);
                return;
            }
            CFSetAddValue(rlt->_rlModes, rlm->_name);
            __CFRunLoopTimerUnlock(rlt);
            __CFRunLoopTimerFireTSRLock();
            __CFRepositionTimerInMode(rlm, rlt, false);
            __CFRunLoopTimerFireTSRUnlock();
            if (!_CFExecutableLinkedOnOrAfter(CFSystemVersionLion)) {
                // Normally we don't do this on behalf of clients, but for
                // backwards compatibility due to the change in timer handling...
                if (rl != CFRunLoopGetCurrent()) CFRunLoopWakeUp(rl);
            }
        }
        if (NULL != rlm) {
            __CFRunLoopModeUnlock(rlm);
        }
    }
    __CFRunLoopUnlock(rl);
}
复制代码

这个方法将将timer对象加到items进行调用,然后这里加了后会在__CFRunLoopDoBlocks里进行消费(调用),拿到对应的item,通过doit进行判断,如果存在则创建一个堆block,引用的对象是timer,然后会进行block的调用。


Observer

observer监听RunLoop的回调状态,只要Runloop的model改变就会通知RunLoop

Source

Source0

包含函数回调指针 signal待处理 wajeup唤醒RunLoop处理事件,处理app内部事件,app自己负责管理的事物


    CFRunLoopSourceContext context = {
        0,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        schedule,
        cancel,
        perform,
    };
    /**
     
     参数一:传递NULL或kCFAllocatorDefault以使用当前默认分配器。
     参数二:优先级索引,指示处理运行循环源的顺序。这里我传0为了的就是自主回调
     参数三:为运行循环源保存上下文信息的结构
     */
    CFRunLoopSourceRef source0 = CFRunLoopSourceCreate(CFAllocatorGetDefault(), 0, &context);
    CFRunLoopRef rlp = CFRunLoopGetCurrent();
    // source --> runloop 指定了mode  那么此时我们source就进入待绪状态
    CFRunLoopAddSource(rlp, source0, kCFRunLoopDefaultMode);
    // 一个执行信号
    CFRunLoopSourceSignal(source0);
    // 唤醒 run loop 防止沉睡状态
    CFRunLoopWakeUp(rlp);
    // 取消 移除
    CFRunLoopRemoveSource(rlp, source0, kCFRunLoopDefaultMode);
    CFRelease(rlp);
复制代码

Source1

match_port 和 函数回调指针 主要用在线程之间通讯

@property (nonatomic, strong) NSPort* subThreadPort;
@property (nonatomic, strong) NSPort* mainThreadPort;

- (void)setupPort{
    self.mainThreadPort = [NSPort port];
    self.mainThreadPort.delegate = self;
    // port - source1 -- runloop
    [[NSRunLoop currentRunLoop] addPort:self.mainThreadPort forMode:NSDefaultRunLoopMode];

    [self task];
}

- (void) task {
    NSThread *thread = [[NSThread alloc] initWithBlock:^{
        self.subThreadPort = [NSPort port];
        self.subThreadPort.delegate = self;
        
        [[NSRunLoop currentRunLoop] addPort:self.subThreadPort forMode:NSDefaultRunLoopMode]; 
        [[NSRunLoop currentRunLoop] run]; "///这里需要加Run因为子线程Runloop默认不开启"
    }];
    
    [thread start];
    // 主线 -- 子线程
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"%@", [NSThread currentThread]); // 3

        NSString *str;
        dispatch_async(dispatch_get_main_queue(), ^{
            // 1
            NSLog(@"%@", [NSThread currentThread]);

        });
    });
}

// 线程之间通讯
// 主线程 -- data
// 子线程 -- data1
// 更加低层 -- 内核
// mach
- (void)handlePortMessage:(id)message {
    NSLog(@"%@", [NSThread currentThread]); // 3 1

    unsigned int count = 0;
    Ivar *ivars = class_copyIvarList([message class], &count);
    for (int i = 0; i<count; i++) {
        
        NSString *name = [NSString stringWithUTF8String:ivar_getName(ivars[i])];
//        NSLog(@"%@",name);
    }
    
    sleep(1);
    if (![[NSThread currentThread] isMainThread]) {

        NSMutableArray* components = [NSMutableArray array];
        NSData* data = [@"woard" dataUsingEncoding:NSUTF8StringEncoding];
        [components addObject:data];
        "主线程往子线程发送消息"
        [self.mainThreadPort sendBeforeDate:[NSDate date] components:components from:self.subThreadPort reserved:0];
    }
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    
    NSMutableArray* components = [NSMutableArray array];
    NSData* data = [@"hello" dataUsingEncoding:NSUTF8StringEncoding];
    [components addObject:data];
    ///"子线程往主线程发送消息"
    [self.subThreadPort sendBeforeDate:[NSDate date] components:components from:self.mainThreadPort reserved:0];
}
复制代码

横线下面为我概括总结正在一步一步学习中,下一篇文章将通过RunLoop的源码进行分析他们是如何实现的

RunLoop总结

Runloop的作用就是来管理和调度线程是他在没有任务的时候不会被销毁。
Runloop与线程的关系是一一对应的,一个线程一次只能执行一个任务,任务执行完毕后线程就会被销毁。对于主线程来说,runloop在程序一启动就默认创建好了子线程Runloop默认不开启。
Runloop 与 线程关系是一对一 与mode关系是一对n 意思是同时可以持有多个model,model里面分忧、Source、Tiemr、Observer 三中item同样也是1对多的关系可以持有多个item,Timer
Observer是以数组的形式存在,而Source 是以集合的形式存在Runloop中。
Source0 的作用是处理app的事物处理 触摸事件等
Source1 的作用是NSPort 来进行线程通讯这种操作偏底层偏内核
observers 的作用是Runloop的model改变的时候会通知Runloop
timer的作用是
Runloop工作的流程是:

  1. 通知observer即将进入Runloop
  2. 通知Observer将要处理Timer
  3. 通知Observer将要处理Source0
  4. 处理Source0
  5. 如果有Source1调到第9步
  6. 通知observer,线程即将进入休眠
  7. 休眠等待唤醒(source0,Timer,外部手动唤醒)
  8. 通知observer线程刚刚被唤醒
  9. 处理Source1,并回到第2步
  10. 通知observer将要推出Runloop

转载于:https://juejin.im/post/5ca31021f265da30a67bd37a

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值