博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
runloop
阅读量:6688 次
发布时间:2019-06-25

本文共 3672 字,大约阅读时间需要 12 分钟。

1、为什么用runloop

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

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

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

你可能感兴趣的文章
Jersey REST 服务中 DELETE 请求无法接收 entity body 作为参数
查看>>
【java_web】web批量分页打印
查看>>
跟益达学Solr5之Facet一瞥
查看>>
Data truncation: Out of range value
查看>>
Java中throws和throw的区别讲解
查看>>
Linux TOP命令详解
查看>>
不算完美的实现了自动化部署的进度实时更新
查看>>
Android2.2 API 中文文档系列(4) —— Manifest
查看>>
js 克隆
查看>>
Spring Boot:Data Rest Service
查看>>
二叉树学习笔记之经典平衡二叉树(AVL树)
查看>>
[C/C++基础知识] 一篇就让你彻底搞懂qsort快速排序的文章
查看>>
Dubbo架构设计详解
查看>>
JMeter基础之一 一个简单的性能测试
查看>>
磁带机Media is unrecognized
查看>>
DH密钥交换非对称加密
查看>>
程序员的量化交易之路(19)--Cointrader之Bar实体(7)
查看>>
存储过程中用到的年,月,周的函数
查看>>
IE7下元素的 'padding-top' 遇到 'clear' 特性在某些情况下复制到 'padding-bottom'
查看>>
IOS开发--常用工具类收集整理(Objective-C)(持续更新)
查看>>