NSTimer 运行机制

1.IOS的Run Loops机制

Run Loops是线程的基础部份,任何线程,包括主结程,都包含了一个run loop对象,Cocoa和CoreFoundation层都有对应的Run Loop实现。

Run loop 对线程的作用,就是用来控制当有事件需要处理的时候,让线程快速响应,而当没有工作的时候,线程改为休息。

本质上Run Loop是一个While死循环。不停地监听事件以及处理事件。我们可以自己写一个While循环来做到这点,但是苹果的封装显然会更好。比如可以有不同的运行模式、不同的接收源和定时源,不工作的时候休息等。

在一个应用的主函数中:

int main(int argc, char * argv[])
{
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

UIApplicationMain()函数就会启动一个主线程,并且自动为它设置一个Run Loop对象,但对除此以外,其它的线程需要明确去设置和启动。

2.适用场景


只有创建一个附属线程的时候,才需要明确去运行一个Run Loop.而且是在确定需要的时候才去设置和启动它,否则就必要了。例如,开一个线程去执行一个明确的长时间的任务,就没有必要。起用Run Loop主要还是为了跟创建的线程可以有更多的交互。

而使用Run Loop一个明显的好处就是:节约计算资源,同时也就节约用电了。因为在没有触发的时候线程是处于休眠状态的,不会消耗CPU资源。

3.结构


一个Run Loop可以接收的事件类型有两种:一种是输入(Input Source);一种是时间资源(time source).前者异步传递事件的,通常消息是来自其它线程或应用发送的;而后者是同步事件的,比如:定时计划,或者定时重复的工作。


runloop.jpg

4.观察者


Run Loop对象的循环过程中可以添加观察者对象。整个Run Loop 在运行过程中发生的事件具体如下:

  1. 通知观察者Run Loop开始了;
  2. 通知观察者任何预设好的时间已经触发;
  3. 通知观察者任何接入端口的输入源准备被触发;
  4. 触发任何非端口方式接入的输入源;
  5. 如果有任何一个基于端口方式的输入淅准备被触发,立即运行被时间,并且跳到9步;
  6. 通知观察者线程准备休息;
  7. 让线程休息,直到以下任何一个事件发生:
    1. 任何一个基于端口的输入淅有事件发生;
    2. 任何一个计时器触发;
    3. 该Run Loop设置的过期时间过时了;
    4. 该Run Loop被明确地唤醒;
  8. 通知观察者线程被唤醒;
  9. 运行待触发的事件:
    1. 如果一个用户自定义的计时器被触发,运行该计时器的事件,并重新运行Run Loop.跳转到第2步;
    2. 如果一个输入源被触发,传递该事件;
    3. 如果该Run Loop被明确唤醒并且没有超时,重新运行Run Loop循环。跳转到第2步;
  10. 通知观察者该Run Loop循环已经退出。

输入源(Input Source)


输入源主要有三种:

  • 基于端口的输入源
  • 自定义输入源
  • selector源

其中selector源就是常用的“performSelector...”方法。

定时源


定时源adpujiu是常用的NSTimer,定时器类;基机制也是基于Run Loop运行的,只是在指定的间隔时间发送消息给需要处理的回调方法。
两种方法:

NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:4.0 targer:self selector:@selector(fireTimer:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];

以及

[NSTimer scheduledTimerWithTimerInterval:10
                                   terget:self
                                   selector:@selector(fireTimer:)
                                   userInfo:nil
                                   repeats:YES];

不过需要注意的是,如果Run Loop没有监视跟定时淅相关模式,那么定时器将不会运行。如果定时器开始时,Run Loop正在处理前面的事件,那么它会等Run Loop处理完了才开始。如果Run Loop不再运行,那么定时器也永远不再启动了。

使用方式


启动方式

  • 无条件启动;
  • 设置超时时间启动,如RunUntilDate方法;
  • 指定某种模式下启动,如RunMode:beforDate:方法;

能出方法

  • 启动时设定好设定超时时间;
  • 显式的停止Run Loop(调用CFRunLoopStop函数);

启动模式

可用模式有 5 种,一般常用的都是default;

  • default
  • connection
  • modal
  • event tracking
  • common modes

例子


在新线程中,注册一个Run Loop的观察者,监听每次循环过程的所有事件;同时启动一个定时器;从日记中可以看到整个Run Loop 的过程,包括启动和结束、每次定时器唤醒时前后的事件、没有任何任务进入休眠的状态。由此可以看出,Run Loop能更加精细地跟整个线程的运行过程交互。

- (void)viewDidLoad {
    [super viewDidLoad];
    // 在新线程中运行:
    [self performSelectorInBackground:@selector(testRunLoop) withObject:ni;];
}
- (void)testRunloop {
   // 获取当前线程的Run Loop
    NSRunLoop *myRunLoop = [NSRunLoop currentRunLoop];
    // 创建一个Run Loop 观察者对象;观察事件为每次循环的所有活动;
    CFRunLoopObserverContext context = {0, (__bridge void *)(self), NULL, NULL, NULL};
    CFRunLoopObserverRef observer = CFRunLoopObserverCreate(kCFAllocatorDefault,
    kCFRunLoopAllActivities, YES, 0, &myRunLoopObserver, &context);
    if (observer)
    {
        // 将Cocoa的NSRunLoop类型转换成Core Foundation的CFRunLoopRef类型
        CFRunLoopRef cfLoop = [myRunLoop getCFRunLoop];
        // 添加观察才对象到该Run Loop 上
        CFRunLoopAddObserver(cfLoop, observer, kCFRunLoopDefaultMode);
    }
    // 创建定时器,每0.1秒触发
    [NSTimer scheduledTimerWithTimeInterval:0.1 target:self
    selector:@selector(doFireTimer:) userInfo:nil repeats:YES];
    // 重复启动Run Loop 5次
    NSInteger loopCount = 5;
    do {
    //启动 Run Loop 开始循环,直到指定的时间才结束,这里就是持续1秒;
    //当调用RunUnitDate方法时,观察者检测到循环已经启动,开始根据循环的各个阶段的事件,调用上面注册的myRunLoopObserver回调函数。
    [myRunLoop runUntiDate:[NSDate dateWithTimIntervalSinceNow:1]];
    // 运行完之后,会再一次调用回调函数,状态是KFRunLoopExit,表示循环结束。
    loopCount--;
    } while(loopCount);
    NSlog(@"The End.");
}
- (void)doFireTimer:(NSTimer *)timer
{
    NSLog(@"fire timer");
}
// Run loop观察者的回调函数:
void myRunLoopObserver(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) {
    switch (activity) {
        case kCFRunLoopEntry:
            NSLog(@"run loop entry");
            break;
        case kCFRunLoopBeforeTimers:
            NSLog(@"run loop before timers");
            break;
        case kCFRunLoopBeforeSources:
            NSLog(@"run loop before sources");
            break;
        case kCFRunLoopBeforeWaiting:
            NSLog(@"run loop before waiting");
            break;
        case kCFRunLoopAfterWaiting:
            NSLog(@"run loop after waiting");
            break;
        case kCFRunLoopExit:
            NSLog(@"run loop exit");
            break;
        default:
            break;
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,233评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,013评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,030评论 0 241
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,827评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,221评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,542评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,814评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,513评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,225评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,497评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,998评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,342评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,986评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,812评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,560评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,461评论 2 266

推荐阅读更多精彩内容