RunLoop源码分析、基本使用场景和常见问题

YY大神的链接
goole ASDK的Runloop超流畅UI思路

Runloop

  • 从字面上看

    • 循环运行 内部核心其实就是一个do while循环
    • 跑圈 就和这个小伙子一样
      和这个小伙子一样跑圈
      BOOL running = YES;
      do{
          // 处理各种事件执行各种任务
      
      }while(running)
  • 基本作用
    • 保持程序的持续运行
    • 处理App中的各种事件(比如触摸事件、定时器事件、Selector事件)
    • 节省CPU资源,提高程序性能,做事的时候跑起来,不做的时候休眠

Runloop与线程

  • 每条线程都有唯一的一个与之对应的runloop对象
  • 主线程的runloop自动创建,子线程的runloop需要手动创建,不创建就没有
  • Runloop在第一次获取时创建,在线程结束时销毁

获取runloop对象

  • Fundation

    • [NSRunLoop currentRunLoop] // 获取当前线程的
    • [NSRunLoop mainRunLoop]// 获取主线程
  • core fundation

    • CFRunLoopGetCurrent();// 获取当前线程
    • CFRunLoopGetMain(); // 获取主线程

源代码分析系列,当我们调用上面的代码时,来看看源码源码下载地址
1.入口

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;
}

CFRunLoopRef CFRunLoopGetCurrent(void) {
    CHECK_FOR_FORK();
    CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop);
    if (rl) return rl;
    return _CFRunLoopGet0(pthread_self());
}



2.看到_CFRunLoopGet0 这个函数没,这就是第二部,内部实现

// 全局字典,key是pthread_t value是CFRunLoopRef
static CFMutableDictionaryRef __CFRunLoops = NULL;
// 访问__CFRunLoops时的锁
static CFLock_t loopsLock = CFLockInit;

// 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();
    }
    __CFLock(&loopsLock);
    // 第一次进来全局字典里面没有任何东西
    if (!__CFRunLoops) {
        __CFUnlock(&loopsLock);
        // 初始化全局字典
        CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
        // 根据主线程创建主线程RunLoop
        CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
        // 加入到全局字典 key main_thread value mainLoop
        CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
        // 和全局__CFRunLoops关联上,然后把临时创建的杀死
        if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
            CFRelease(dict);
        }
        CFRelease(mainLoop);
        __CFLock(&loopsLock);
    }
    // 如果全局字典存在,根据线程取RunLoop
    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) {
            // 根据线程key把刚创建的关联上
            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);
    }
    // 注册一个回调,当线程销毁时,通知销毁RunLoop
    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;
}



大致创建逻辑就是上面注释,你不需要显示alloc,你只需要获取就行了,内部已经有创建的逻辑,没错,RunLoop对象就搞定了,下面来看看对象到底是什么结构

RunLoop相关类

  • CFRunLoopRef
  • CFRunLoopModeRef
  • CFRunLoopSourceRef
  • CFRunLoopTimerRef
  • CFRunLoopObserverRef
    这里写图片描述

CFRunLoopModeRef代表RunLoop的运行模式

  • 一个 RunLoop 包含若干个 Mode,每个Mode又包含若干个Source/Timer/Observer
  • 每次RunLoop启动时,只能指定其中一个 Mode,这个Mode被称作 CurrentMode

  • 如果需要切换Mode,只能退出Loop,再重新指定一个Mode进入

  • 这样做主要是为了分隔开不同组的Source/Timer/Observer,让其互不影响

系统注册了五个Mode(可以打断点看调用栈)

1.kCFRunLoopDefaultMode:App的默认Mode,通常主线程实在这个Mode
2.UITrackingRunLoopMode: 页面跟踪Mode,用于滚动追踪触摸滑动,保证页面滑动式不受其他Mode影响
NSTimer默认是加到DefaultMode里面的,当滚动式切换到这个模式,所以之前的Timer事件不再调用

3.UIInitializationRunLoopMode:在刚启动App时第一次进入就是这个Mode,完成之后不再用
4.GSEventReveiveRunLoopMode:系统事件的内部Mode,通常用不到

5.CFRunLoopCommonModes:这个是一个占位用的Mode,不是一个真正的Mode,
用来标示切换不同Mode时是否加有这个字段的Mode还是能继续接受事件

CFRunLoopSourceRef是事件源(输入源)

理论分Source:

  • Port-Based Sources 基于Mach port内核/其他线程事件源
  • Custom Input Sources 自定义基本不用
  • Cocoa Perform Selector Sources [Self PerformSele…]

按调用栈分Source:

  • Source0:非基于Port
  • Source1:基于Port,通过内核和其他线程通信,接受,分发系统事件

CFRunLoopTimerRef)

这个东西就是一个定时器,通过设置特定的时间间隔来给叫醒RunLoop处理事件

CFRunLoopObserverRef

CFRunLoopObserverRef是观察者,能够监听RunLoop的状态改变

typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
        kCFRunLoopEntry = (1UL << 0),// 即将进入RunLoop 1
        kCFRunLoopBeforeTimers = (1UL << 1),// 即将处理Timer事件 2
        kCFRunLoopBeforeSources = (1UL << 2), // 即将处理事件源 4
        kCFRunLoopBeforeWaiting = (1UL << 5),// 即将进入休眠 32
        kCFRunLoopAfterWaiting = (1UL << 6),// 即将进入从休眠中醒来 64
        kCFRunLoopExit = (1UL << 7),// 退出 128
        kCFRunLoopAllActivities = 0x0FFFFFFFU // 上述所有模式监听枚举
    };



上述提到的Source/Timer/OBserver就是一个ModeItem,如果一个Mode中一个Item都没有,RunLoop会自动退出,这就是后面要提到的常驻线程创建方式,先看看RunLoop和Mode两者的结构,然后再举例子对上面的行为进行分析

struct __CFRunLoop {
        CFRuntimeBase _base;
        CFMutableSetRef _commonModes; // 名字Default/Tracking
        CFMutableSetRef _commonModeItems;// Timer/Source/Observer
        CFRunLoopModeRef _currentMode;// 当前Mode
        CFMutableSetRef _modes;//一个RunLoop下有多个Mode
    };

    struct __CFRunLoopMode {
        CFRuntimeBase _base;
        pthread_mutex_t _lock;  /* must have the run loop locked before locking this */
        CFStringRef _name; // 用来存放到RunLoop中_commonModes/_modes的字段
        Boolean _stopped;
        char _padding[3];
        CFMutableSetRef _sources0;// 非port事件
        CFMutableSetRef _sources1;// port事件
        CFMutableArrayRef _observers;// 观察者
        CFMutableArrayRef _timers;// timer
        ......
    }



个人理解下,RunLoop结构体中有个modes用来存放所有可切换的Mode,例如Default/Tracking等,currentMode就指定当前在运作的Mode,CommandModes就是存放着无论你切换哪个Mode,你都需要同步_commonModeItems下所有事件的ModeName,你可以把Mode看做一个个的属于你自己的任务,而commentMode看做每个人都要做的公共任务,当你有公共任务的时候也就是栈顶有Commentmode的时候,你做自己的子线任务的时候,同时需要做栈顶的公共任务

实例1Timer

// 方法1
    //  on the current run loop in the default mode
    // 创建的时候是加到NSDefaultRunLoopMode中的
//    [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
    // 方法2
    // 其实和方法一是一样的,只是展开来写罢了
    NSTimer *timer = [NSTimer timerWithTimeInterval:2.3 target:self selector:@selector(run) userInfo:nil repeats:YES];
//    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];

    // 为了在滚动的时候和不滚动的时候都进行调用NSTimer
    // 只需要把Timer加到当前RunLoop中的CommonModes Set容器里面去就好了,无论切换什么模式,都会同步该Timer
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];



默认情况下我们创建的Timer只会在Default状态下执行,当我们滚动的时候Timer就会不调用事件,原因就是RunLoop中它默认加到了NSDefaultMode中,当滚动的时候切换到NSTrackingMode的时候,NSTimer自然就不执行了,这是因为RunLoop只会在一个Mode下运行,当切换Mode的时候需要重启Loop,而且事件不会同步,除非你把事件加到栈顶的CommonModes里面去,这样就可以在当前RunLoop下的所有模式下共享执行了

实例2Observer,可以在事件处理前进行一些拦截

//    typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
//        kCFRunLoopEntry = (1UL << 0),// 即将进入RunLoop 1
//        kCFRunLoopBeforeTimers = (1UL << 1),// 即将处理Timer事件 2
//        kCFRunLoopBeforeSources = (1UL << 2), // 即将处理事件源 4
//        kCFRunLoopBeforeWaiting = (1UL << 5),// 即将进入休眠 32
//        kCFRunLoopAfterWaiting = (1UL << 6),// 即将进入从休眠中醒来 64
//        kCFRunLoopExit = (1UL << 7),// 退出 128
//        kCFRunLoopAllActivities = 0x0FFFFFFFU // 上述所有模式监听枚举
//    };
    CFRunLoopObserverRef observe = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
        if (activity == kCFRunLoopExit) {
            NSLog(@"即将退出runloop%lu",activity);
        }
        else if (activity == kCFRunLoopBeforeWaiting)
        {
            NSLog(@"即将进入休眠%lu",activity);

        }
        else if (activity == kCFRunLoopAfterWaiting)
        {
            NSLog(@"刚从休眠中唤醒%lu",activity);

        }else if (activity == kCFRunLoopBeforeSources)
        {
            NSLog(@"即将处理 Source%lu",activity);

        }else if (activity == kCFRunLoopBeforeTimers)
        {
            NSLog(@"即将处理 Timer%lu",activity);

        }
        else if (activity == kCFRunLoopEntry)
        {
            NSLog(@"即将进入Loop%lu",activity);

        }


    });

    CFRunLoopAddObserver(CFRunLoopGetCurrent(), observe, kCFRunLoopDefaultMode);

    /*
     CF的内存管理(Core Foundation)
     1.凡是带有Create、Copy、Retain等字眼的函数,创建出来的对象,都需要在最后做一次release
     * 比如CFRunLoopObserverCreate
     2.release函数:CFRelease(对象);
     */
    CFRelease(observe);

调用栈

实例3 AFN2x和YYKit下 的常驻线程

// YYKit常驻线程
/// Network thread entry point.
+ (void)_networkThreadMain:(id)object {
    // 常规做法最外层也用autorelease包起来
    @autoreleasepool {
        [[NSThread currentThread] setName:@"com.ibireme.yykit.webimage.request"];
        NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
        // 给创建的loop添加任意一个item
        [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
        [runLoop run];
    }
}

/// Global image request network thread, used by NSURLConnection delegate.
+ (NSThread *)_networkThread {
    static NSThread *thread = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        thread = [[NSThread alloc] initWithTarget:self selector:@selector(_networkThreadMain:) object:nil];
        if ([thread respondsToSelector:@selector(setQualityOfService:)]) {
            thread.qualityOfService = NSQualityOfServiceBackground;
        }
        [thread start];
    });
    return thread;
}



这里的做法是创建 一个RunLoop,但是如果没有添加任何Obserer/Timer/Source,Runloop默认你是会退出的,要进入循环必须加一个item

RunLoop整体逻辑

这里写图片描述
这里写图片描述

这个图的前提需要注意的是,需要检查ModeItem是否为空,是的话直接退出Loop

源码分析

//1.直接调用Run方法
void CFRunLoopRun(void) {   /* DOES CALLOUT */
    do {
        result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);

    } while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}
// 通过指定Mode调用Run方法
SInt32 CFRunLoopRunInMode(CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {     /* DOES CALLOUT */
    return CFRunLoopRunSpecific(CFRunLoopGetCurrent(), modeName, seconds, returnAfterSourceHandled);
}
// 2.启动
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {     /* DOES CALLOUT */
    //3. 根据RunloopModeName找到Mode
    CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false);
    //4. 如果Mode为空里面没有任何的item直接返回
    if (NULL == currentMode || __CFRunLoopModeIsEmpty(rl, currentMode, rl->_currentMode)) {
    return kCFRunLoopRunFinished;
    }
    //5. 如果不为空 先通知Observer:即将进入Loop kCFRunLoopEntry = (1UL << 0),// 即将进入RunLoop 1
    if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
    //6. 进入核心函数进行跑圈loop
    result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
    //end: kCFRunLoopExit = (1UL << 7),// 退出 128
    if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
    return result;
}
// 核心跑圈代码
/* rl, rlm are locked on entrance and exit */
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
    uint64_t startTSR = mach_absolute_time();
    __CFPortSet waitSet = rlm->_portSet;

        __CFRunLoopUnsetIgnoreWakeUps(rl);
        // 8.通知Observer
        // kCFRunLoopBeforeTimers = (1UL << 1),// 即将处理Timer事件 2
        // kCFRunLoopBeforeSources = (1UL << 2), // 即将处理事件源 4

        if (rlm->_observerMask & kCFRunLoopBeforeTimers) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
        if (rlm->_observerMask & kCFRunLoopBeforeSources) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
    // 任务加入Block处理
    __CFRunLoopDoBlocks(rl, rlm);
        //9. 处理Source0事件 执行事件加入block
        Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
        if (sourceHandledThisLoop) {
            __CFRunLoopDoBlocks(rl, rlm);
    }

        Boolean poll = sourceHandledThisLoop || (0ULL == timeout_context->termTSR);

        //10. 如果有source1进来,基于port的消息进来 处理Source1 goto语句跳转到下面
        if (MACH_PORT_NULL != dispatchPort && !didDispatchPortLastTime) {
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
            msg = (mach_msg_header_t *)msg_buffer;
            if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
                // 跳转
                goto handle_msg;
            }
        }

        didDispatchPortLastTime = false;
    //11. 通知ObserverseRunloop即将进入休眠kCFRunLoopBeforeWaiting
    if (!poll && (rlm->_observerMask & kCFRunLoopBeforeWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);

#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
#if USE_DISPATCH_SOURCE_FOR_TIMERS
        do {
           //12. 调用mach方法等待消息的接受,线程进入休眠,当port传来source1的时候,当Timer时间到了,当Loop超时了
           // 当被显示唤醒的时候
            __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);

        } while (1);
    //13. 通知Runloop线程被刚刚唤醒
    if (!poll && (rlm->_observerMask & kCFRunLoopAfterWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
    //14. 处理消息
        handle_msg:;
        __CFRunLoopSetIgnoreWakeUps(rl);

//15. 如果Timer到了 触发Timer回调
            CFRUNLOOP_WAKEUP_FOR_TIMER();
            if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
                __CFArmNextTimerInMode(rlm, rl);
            }
        }
//16. 如果是由dispatch到main_queue的block,执行block
            CFRUNLOOP_WAKEUP_FOR_DISPATCH();
            __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);

//17. 如果一个source1发出事件,处理source1
            CFRUNLOOP_WAKEUP_FOR_SOURCE();
            // Despite the name, this works for windows handles as well
            CFRunLoopSourceRef rls = __CFRunLoopModeFindSourceForMachPort(rl, rlm, livePort);
        sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply) || sourceHandledThisLoop;
        sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls) || sourceHandledThisLoop;
    __CFRunLoopDoBlocks(rl, rlm);

//18.处理是否继续循环 
    if (sourceHandledThisLoop && stopAfterHandle) {
    // 进入loop时参数说处理完事件就返回。
        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)) {
    // source/timer/observer为空一个都没了
        retVal = kCFRunLoopRunFinished;
    }
    } while (0 == retVal);// 还是0,继续跑

    return retVal;
}

关于Runloop的一些问题

  • 什么是RunLoop?
    内部就是一个do - While循环,在这个循环内部不断处理各种事件
    (Source、Timer、Observer)
    一个线程对应一个RunLoop,主线程RunLoop默认已经启动,子线程的RunLoop得手动启动
    RunLoop只能选择一个模式,如果当前模式中没有Source、Timer、Obeserver直接突出

  • 你在开发中怎么使用RunLoop?
    1.AF2x.和YYKit常驻线程
    在子线程中开启定时器
    子线程中长期监控一些行为(扫描网络、沙盒、语音监控)

    2.可以控制定时器在特定模式下执行

    3.可以让某些事件在特定模式下执行(Performance…inmodes(mode参数))
    4.可以添加Observer监听RunLoop的状态,比如监听点击事件处理前处理某些事情

  • 自动释放池什么时候释放
    在RunLoop睡眠之前释放(kCFRunLoopBeforeWaiting)

总结

4.什么是Runloop

Runloop他的本质就是一个do,while循环
(1)保持程序的持续运行,当有事做时做事,
(2)负责监听处理App中的各种事件(比如触摸事件、定时器事件、Selector事件)
(3)节省CPU资源,提高程序性能,做事的时候跑起来,不做的时候休眠。

每个线程都有一个Run Loop,主线程的Run Loop会在App运行时自动运行,子线程中需要手动获取运行,创建是发生在第一次获取时,RunLoop 的销毁是发生在线程结束时。

一个 RunLoop 包含若干个 Mode,每个 Mode 又包含若干个 Source/Timer/Observer。每次调用 RunLoop 的主函数时,只能指定其中一个 Mode,这个Mode被称作 CurrentMode。如果需要切换 Mode,只能退出 Loop,再重新指定一个 Mode 进入。这样做主要是为了分隔开不同组的 Source/Timer/Observer,让其互不影响。当创建的loop,没有任何上面的属性,就会直接退出。
Run的时候必须制定mode,那么这里有个知识点是CommonMode,不能指定该Mode进行运行,但是可以把事件标记到该属性,那么无论切换到哪个Mode都会执行

Apple应用
1.AutoreleasePool
App启动后,苹果在主线程 RunLoop 里注册了两个 Observer,第一个 Observer 监视的事件是 Entry(即将进入Loop),执行方法去创建pool,优先级最高,第二个是BeforeWaiting(准备进入休眠) 时调用pop and push,先清除旧pool,然后创建新pool,或者再退出的时候释放自动释放池子,优先级最低

2.启动时注册,处理事件,nstimer,UI,渲染

3.网络请求框架下的线程通信

开发者应用:
1.常驻线程 处理网络请求的回调
2.Common在不同模式下也执行NSTimer
3.添加Observe属性的观察,在即将进入休眠的ASDK的原理,以下就是AS框架的Runloop应用,这里把排版,绘制等操作放到异步做,然后存储起来
在kCFRunLoopBeforeWaiting和kCFRunLoopExit两个activity的回调中将之前异步完成的工作同步到主线程中去。
ASDK goole的UI框架原理
一个自称用cell实现Runloop应用的人

__unsafe_unretained __typeof__(self) weakSelf = self;
    void (^handlerBlock) (CFRunLoopObserverRef observer, CFRunLoopActivity activity) = ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
      [weakSelf processQueue];
    };
    _runLoopObserver = CFRunLoopObserverCreateWithHandler(NULL, kCFRunLoopBeforeWaiting, true, 0, handlerBlock);
    CFRunLoopAddObserver(_runLoop, _runLoopObserver,  kCFRunLoopCommonModes);

结构:

struct __CFRunLoopMode {
    CFStringRef _name;            // Mode Name, 例如 @"kCFRunLoopDefaultMode"
    CFMutableSetRef _sources0;    // Set
    CFMutableSetRef _sources1;    // Set
    CFMutableArrayRef _observers; // Array
    CFMutableArrayRef _timers;    // Array
    ...
};
struct __CFRunLoop {
    CFMutableSetRef _commonModes;     // Set
    CFMutableSetRef _commonModeItems; // Set<Source/Observer/Timer>
    CFRunLoopModeRef _currentMode;    // Current Runloop Mode
    CFMutableSetRef _modes;           // Set
};
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值