美文网首页
RunTime和Runloop的理解

RunTime和Runloop的理解

作者: 微笑_d797 | 来源:发表于2022-03-01 22:08 被阅读0次

    RunTime

    Runtime是一套比较底层的纯C语言API,他将更多的决策从编译时他UI吃到了运行时,我们平时写的iOS代码在程序运行的过程中都会转成Runtime的C语言代码,运行时系统充当着OC的操作系统,他使语言能够工作

    我们平时用Runtime可以给系统类动态添加方法属性方法交换等

    objc_msg_send

    oc中函数调用在底层是改为了objc_msg_send方法

    sel:类成员方法的指针

    imp:函数指针保存了方法地址

    method: 不透明模型里面包括sel和imp和methodtype 一个类(Class)持有一个分发表,在运行期分发消息,表中的每一个实体代表一个方法(Method),它的名字叫做选择子(SEL),对应着一种方法实现(IMP)。

    struct objc_method {
        SEL method_name; 
        char *method_types;
        IMP method_imp;
    };
    
    Person * p = [[Person alloc] init];
    [p run];
    

    其中run方法被编译成了 sel_registerName("run"),然后该方法会经过一些类 sel查找IMP的过程


    首先是先进行快速查找

    每个类的构成有一个cache,存储类的selector和IMP,IMP和selector会组成一个哈希表,然后通过hash直接查找会非常快,当查找一个方法时,首先找cache,如果有会直接返回,如果没有则会经历一个复杂而又缓慢(慢速)的过程,找到了会继续往cache里面存

    当执行objc_msg_send时
    会先执行 Lookup_NilOrTagged 进行指针优化然后执行 LGetIsaDone

    然后进行CacheLookup Normal流程 如果缓存里有则call imp 如果没有则调用objc_msgSend_uncached


    此时进入慢速查找

    MethodTableLookup

    __class_lookupMethodAndLoadCache3

    lookupImpOrFowrward

    如果缓存存在往缓存里取IMP,如果不存在往下走判断类是不是已知类然后在走相应的流程 ,判断完毕后依然需要从缓存里面取一遍,这样做事为了并发设置及的以及remap(cls)重映射,进行自己->父类->NSObject->getMethodNoSuper_nolock->log_and_fill_cache(将该IMP缓存)


    如果都没有找到那么进入到消息转发机制
    resolveinstanceMethod 返回true 并添加新的消息进行处理添加方法重新进入慢速查找
    没有处理则出发快速转发机制forwardTarget出发消息转发机制来实现快速转发,如果依然没有处理则进行慢速转发,方法签名+消息签名

    isa走位

    我们创建的一个对象或实例其实就是一个struct objc_object结构体,而我们常用的id也就是这个结构体的指针。这个结构体只有一个成员变量,这是一个Class类型的变量isa,也是一个结构体指针,那这个指针又指向什么呢?面向对象中每一个对象都必须依赖一个类来创建,因此对象的isa指针就指向对象所属的类根据这个类模板能够创建出实例变量、实例方法等。

    struct objc_classs结构体里存放的数据称为元数据(metadata),通过成员变量的名称我们可以猜测里面存放有指向父类的指针、类的名字、版本、实例大小、实例变量列表、方法列表、缓存、遵守的协议列表等,这些信息就足够创建一个实例了,该结构体的第一个成员变量也是isa指针,这就说明了Class本身其实也是一个对象,我们称之为类对象,类对象在编译期产生用于创建实例对象

    RunLoop

    Runlopp是苹果推出的一个消息处理机制,一般情况下线程处理完任务就会退出,但是因为runloop在线程中构成了一个消息循环,使其一直有任务执行。然后线程不会退出,这个机制叫做Runloop

    Runlopp有多个model里,但他只能在一个model里运行,model里有items,其中items包括timer,observer,source0,source1,source0处理的app的周期事件比如点击等,souce1主要是用来进行跨线程通讯比较偏底层

    源码分析

    Runloop工作流程是
    1.通知Observer即将进入Runloop
    2.通知Observer处理timer
    3.通知Observer处理source
    4.处理Source0
    5.如果有Souce1 跳到第9步
    6.通知observer线程即将休眠
    7.休眠等待唤醒
    8.处理唤醒时收到的消息回到2
    9.通知observer,即将推出Runloop

    下面通过源码的形势来进行分析

    首先外部调用 [NSRunloop current] run]; 底层调用

    void CFRunLoopRun(void) {    /* DOES CALLOUT */
        int32_t result;
        do {
            result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
            CHECK_FOR_FORK();
        } while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
    }
    Runloop源码第2678行
    
    
    struct _block_item {
        struct _block_item *_next;
        CFTypeRef _mode;    /// CFString or CFSet
        void (^_block)(void);
    };
    
    typedef struct _per_run_data {
        uint32_t a;
        uint32_t b;
        uint32_t stopped;
        uint32_t ignoreWakeUps;
    } _per_run_data;
    
    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;
        CFMutableSetRef _commonModeItems;
        CFRunLoopModeRef _currentMode;
        CFMutableSetRef _modes;
        struct _block_item *_blocks_head;
        struct _block_item *_blocks_tail;
        CFAbsoluteTime _runTime;
        CFAbsoluteTime _sleepTime;
        CFTypeRef _counterpart;
    };
    
    
    

    此时进入do while循环 里面一直执行 CFRunLoopRunSpecific函数下面分析这个函数

    SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {     /* DOES CALLOUT */
        CHECK_FOR_FORK();
        ///判断rloop是否被释放释放的话返回 kCFRunLoopRunFinished 结束runloop
        if (__CFRunLoopIsDeallocating(rl)) return kCFRunLoopRunFinished;
        __CFRunLoopLock(rl);
        ///取当前runnloop的model如果model为nil或为空那么退出runloop
        CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false);
        if (NULL == currentMode || __CFRunLoopModeIsEmpty(rl, currentMode, rl->_currentMode)) {
            Boolean did = false;
            if (currentMode) __CFRunLoopModeUnlock(currentMode);
            __CFRunLoopUnlock(rl);
            return did ? kCFRunLoopRunHandledSource : kCFRunLoopRunFinished;
        }
        
        ///如果走到这里说明渠道了runloop‘
        ///_per_run_data是用来描述当前CFRunLoop的状态的,有三种状态:初始状态,wake,stop三种。注意到 volatile 这个关键字,它的意思是告诉编译器不要优化这个变量,要每次都从内存中读取该变量。
        volatile _per_run_data *previousPerRun = __CFRunLoopPushPerRunData(rl);
        CFRunLoopModeRef previousMode = rl->_currentMode;
        rl->_currentMode = currentMode;
        int32_t result = kCFRunLoopRunFinished;
        ///通知observer进入runloop
        if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
        ///因为__CFRunLoopRun 处理observer timer souce 等
        result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
        
        ///通知observer退出runloop
        if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
        
        __CFRunLoopModeUnlock(currentMode);
        __CFRunLoopPopPerRunData(rl, previousPerRun);
        rl->_currentMode = previousMode;
        __CFRunLoopUnlock(rl);
        return result;
    }
    

    我们看出处理Runloop的函数全部都在__CFRunLoopRun函数里

     /// 内部函数,进入loop
        __CFRunLoopRun(runloop, currentMode, seconds, returnAfterSourceHandled) {
             
            Boolean sourceHandledThisLoop = NO;
            int retVal = 0;
            do {
                __CFRunLoopUnsetIgnoreWakeUps ///唤醒runloop
                
                /// 2. 通知 Observers: RunLoop 即将触发 Timer 回调。
                __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeTimers);
                /// 3. 通知 Observers: RunLoop 即将触发 Source0 (非port) 回调。
                __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeSources);
                /// 执行被加入的block
                __CFRunLoopDoBlocks(runloop, currentMode);
                 
                /// 4. RunLoop 触发 Source0 (非port) 回调。
                sourceHandledThisLoop = __CFRunLoopDoSources0(runloop, currentMode, stopAfterHandle);
                /// 执行被加入的block
                __CFRunLoopDoBlocks(runloop, currentMode);
      
                /// 5. 如果有 Source1 (基于port) 处于 ready 状态,直接处理这个 Source1 然后跳转去处理消息。
                if (__Source0DidDispatchPortLastTime) {
                    Boolean hasMsg = __CFRunLoopServiceMachPort(dispatchPort, &msg)
                    if (hasMsg) goto handle_msg;
                }
                 
                /// 通知 Observers: RunLoop 的线程即将进入休眠(sleep)。
                if (!sourceHandledThisLoop) {
                    __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeWaiting);
                }
                 
                /// 7. 调用 mach_msg 等待接受 mach_port 的消息。线程将进入休眠, 直到被下面某一个事件唤醒。
                /// ? 一个基于 port 的Source 的事件。
                /// ? 一个 Timer 到时间了
                /// ? RunLoop 自身的超时时间到了
                /// ? 被其他什么调用者手动唤醒
                __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort) {
                    mach_msg(msg, MACH_RCV_MSG, port); /// thread wait for receive msg
                }
      
                /// 8. 通知 Observers: RunLoop 的线程刚刚被唤醒了。
                __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopAfterWaiting);
                 
                /// 收到消息,处理消息。
                handle_msg:
      
                /// 9.1 如果一个 Timer 到时间了,触发这个Timer的回调。
                if (msg_is_timer) {
                    __CFRunLoopDoTimers(runloop, currentMode, mach_absolute_time())
                } 
      
                /// 9.2 如果有dispatch到main_queue的block,执行block。
                else if (msg_is_dispatch) {
                    __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
                } 
      
                /// 9.3 如果一个 Source1 (基于port) 发出事件了,处理这个事件
                else {
                    CFRunLoopSourceRef source1 = __CFRunLoopModeFindSourceForMachPort(runloop, currentMode, livePort);
                    sourceHandledThisLoop = __CFRunLoopDoSource1(runloop, currentMode, source1, msg);
                    if (sourceHandledThisLoop) {
                        mach_msg(reply, MACH_SEND_MSG, reply);
                    }
                }
                 
                /// 执行加入到Loop的block
                __CFRunLoopDoBlocks(runloop, currentMode);
                 
      
                if (sourceHandledThisLoop && stopAfterHandle) {
                    /// 进入loop时参数说处理完事件就返回。
                    retVal = kCFRunLoopRunHandledSource;
                } else if (timeout) {
                    /// 超出传入参数标记的超时时间了
                    retVal = kCFRunLoopRunTimedOut;
                } else if (__CFRunLoopIsStopped(runloop)) {
                    /// 被外部调用者强制停止了
                    retVal = kCFRunLoopRunStopped;
                } else if (__CFRunLoopModeIsEmpty(runloop, currentMode)) {
                    /// source/timer/observer一个都没有了
                    retVal = kCFRunLoopRunFinished;
                }
                 
                /// 如果没超时,mode里没空,loop也没被停止,那继续loop。
            } while (retVal == 0);
        }
    

    所以runloop的运行流程全部体现在代码及注释里。
    在源码里我们发现__CFRunLoopDoBlocks使用来处理source0 timer 等

    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;
        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 相等
                /// 事务就能执行
    
                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;
            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);
                    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;
    }
    

    最终函数通过CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK(block);进行回调

    addTimer和addobserver源码就是经过一系列判断然后将observer复制给runloop的currentmodel的observer/timer

    void CFRunLoopAddObserver(CFRunLoopRef rl, CFRunLoopObserverRef rlo, CFStringRef modeName) {
        CHECK_FOR_FORK();
        CFRunLoopModeRef rlm;
        if (__CFRunLoopIsDeallocating(rl)) return;
        if (!__CFIsValid(rlo) || (NULL != rlo->_runLoop && rlo->_runLoop != rl)) return;
        __CFRunLoopLock(rl);
        if (modeName == kCFRunLoopCommonModes) {
            CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL;
            if (NULL == rl->_commonModeItems) {
                rl->_commonModeItems = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
            }
            CFSetAddValue(rl->_commonModeItems, rlo);
            if (NULL != set) {
                CFTypeRef context[2] = {rl, rlo};
                /* add new item to all common-modes */
                CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context);
                CFRelease(set);
            }
        } else {
            rlm = __CFRunLoopFindMode(rl, modeName, true);
            if (NULL != rlm && NULL == rlm->_observers) {
                rlm->_observers = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks);
            }
            if (NULL != rlm && !CFArrayContainsValue(rlm->_observers, CFRangeMake(0, CFArrayGetCount(rlm->_observers)), rlo)) {
                Boolean inserted = false;
                for (CFIndex idx = CFArrayGetCount(rlm->_observers); idx--; ) {
                    CFRunLoopObserverRef obs = (CFRunLoopObserverRef)CFArrayGetValueAtIndex(rlm->_observers, idx);
                    if (obs->_order <= rlo->_order) {
                        CFArrayInsertValueAtIndex(rlm->_observers, idx + 1, rlo);
                        inserted = true;
                        break;
                    }
                }
                if (!inserted) {
                    CFArrayInsertValueAtIndex(rlm->_observers, 0, rlo);
                }
                rlm->_observerMask |= rlo->_activities;
                __CFRunLoopObserverSchedule(rlo, rl, rlm);
            }
            if (NULL != rlm) {
                __CFRunLoopModeUnlock(rlm);
            }
        }
        __CFRunLoopUnlock(rl);
    }
    

    相关文章

      网友评论

          本文标题:RunTime和Runloop的理解

          本文链接:https://www.haomeiwen.com/subject/udzzlrtx.html