runloop

1、为什么用runloop

一般来讲,一个线程一次只能执行一个任务,执行完成后线程就会退出。

如果我们需要一个机制,让线程能随时处理事件但并不退出,这种模型通常被称作 Event Loop

RunLoop 实际上就是一个对象,这个对象管理了其需要处理的事件和消息,

线程执行了这个函数后,就会一直处于这个函数内部 “接受消息->等待->处理” 的循环中,直到这个循环结束(比如传入 quit 的消息),函数返回。

2、runloop创建方式

CFRunLoop 是基于 pthread 来管理的。

线程和 RunLoop 之间是一一对应的,其关系是保存在一个全局的 Dictionary 里。

/// 全局的Dictionary,key 是 pthread_t, value 是 CFRunLoopRef
static CFMutableDictionaryRef loopsDic;
/// 访问 loopsDic 时的锁
static CFSpinLock_t loopsLock;
/// 获取一个 pthread 对应的 RunLoop。
CFRunLoopRef _CFRunLoopGet(pthread_t thread) {
    OSSpinLockLock(&loopsLock);
    if (!loopsDic) {
        // 第一次进入时,初始化全局Dic,并先为主线程创建一个 RunLoop。
        loopsDic = CFDictionaryCreateMutable();
        CFRunLoopRef mainLoop = _CFRunLoopCreate();
        CFDictionarySetValue(loopsDic, pthread_main_thread_np(), mainLoop);
    }
    /// 直接从 Dictionary 里获取。
    CFRunLoopRef loop = CFDictionaryGetValue(loopsDic, thread));
    if (!loop) {
        /// 取不到时,创建一个
        loop = _CFRunLoopCreate();
        CFDictionarySetValue(loopsDic, thread, loop);
        /// 注册一个回调,当线程销毁时,顺便也销毁其对应的 RunLoop。
        _CFSetTSD(..., thread, loop, __CFFinalizeRunLoop);
    }
    OSSpinLockUnLock(&loopsLock);
    return loop;
}
CFRunLoopRef CFRunLoopGetMain() {
    return _CFRunLoopGet(pthread_main_thread_np());
}
CFRunLoopRef CFRunLoopGetCurrent() {
    return _CFRunLoopGet(pthread_self());
}
3、runloop的model以及对应的触发时机
一个 RunLoop 包含若干个 Mode,每个 Mode 又包含若干个 Source/Timer/Observer。每次调用 RunLoop 的主函数时,只能指定其中一个 Mode,这个Mode被称作 CurrentMode。如果需要切换 Mode,只能退出 Loop,再重新指定一个 Mode 进入。这样做主要是为了分隔开不同组的 Source/Timer/Observer,让其互不影响。
• Source0 只包含了一个回调(函数指针),它并不能主动触发事件。使用时,你需要先调用 CFRunLoopSourceSignal(source),将这个 Source 标记为待处理,然后手动调用 CFRunLoopWakeUp(runloop) 来唤醒 RunLoop,让其处理这个事件。
• Source1 包含了一个 mach_port 和一个回调(函数指针),被用于通过内核和其他线程相互发送消息。这种 Source 能主动唤醒 RunLoop 的线程,
每个 Observer 都包含了一个回调(函数指针),当 RunLoop 的状态发生变化时,观察者就能通过回调接受到这个变化。
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry         = (1UL << 0), // 即将进入Loop
    kCFRunLoopBeforeTimers  = (1UL << 1), // 即将处理 Timer
    kCFRunLoopBeforeSources = (1UL << 2), // 即将处理 Source
    kCFRunLoopBeforeWaiting = (1UL << 5), // 即将进入休眠
    kCFRunLoopAfterWaiting  = (1UL << 6), // 刚从休眠中唤醒
    kCFRunLoopExit          = (1UL << 7), // 即将退出Loop
};
上面的 Source/Timer/Observer 被统称为 mode item,如果一个 mode 中一个 item 都没有,则 RunLoop 会直接退出,不进入循环。

- (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray<NSRunLoopMode> *)modes;//将一个timer事件加入1个或者多个mode中

4、runloop工作原理
 
 

PerformSelecter

当调用 NSObject 的 performSelecter:afterDelay: 后,实际上其内部会创建一个 Timer 并添加到当前线程的 RunLoop 中。所以如果当前线程没有 RunLoop,则这个方法会失效。

当调用 performSelector:onThread: 时,实际上其会创建一个source0 加到对应的线程去,同样的,如果对应线程没有 RunLoop 该方法也会失效。

 

定时异步回调

用runloop的observer,每次进入到beforeWating的时候调用block

  • (BOOL)runUntilBlock:(BOOL(^)())block timeout:(NSTimeInterval)timeout{

      __block Boolean fulfilled = NO;

      void(^beforeWaiting)(CFRunLoopObserverRef observer,CFRunLoopActivity activity) = ^(CFRunLoopObserverRef observer,CFRunLoopActivity activity) {

   fulfilled = block();

   if (fulfilled) {

   CFRunLoopStop(CRRunLoopGetCurrent());

    }

       };

       CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(NULL, CFRunLoopBeforeWaiting,true,0,beforeWating);

                    

   CFRunLoopAddObserver(CFRunLoopGetCurrent(),observer,KCFRunLoopDefaultMode);

        CFRunLoopRunInMode(kCFRunLoopDefaultMode,timeout,false);

 CFRunLoopRemoveObserver(CFRunLoopGetCurrent(),observer,KCFRunLoopDefaultMode);

CFRelease(observer);

      return fulfilled;

}

 

runloop和dispatch的联系:runloop会在周期内询问dispatch主队列是否有任务(即block块代码)需要完成,并当作event实践处理

 

runloop有一个timer,但和diapacth的timer不一样,场景有(NSTimer,performSelector: after)

 

runloop和autoreleasePool联系:autorealeasePool块执行完成,回调用autoReleasePoolPage pop方法,在runloop的beforeWating和afterWating之间执行此方法,执行release操作

转载于:https://www.cnblogs.com/diyigechengxu/p/9212278.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值