一:什麼是RunLoop
(1)從字面意思看,運行循環、跑圈。
(2)保持程序持續運行,處理App中的各類事件包括觸摸事件、定時器事件、Selector事件。
(3)節省CPU資源,提高程序的性能,該做事的時候做事,該休息的時候休息。
二:RunLoop與線程
(1)每條線程都有唯一的一個與之對應的RunLoop對象。
(2)主線程的RunLoop已經自動創建好了,子線程的RunLoop需要主動創建。
(3)RunLoop在第一次獲取時創建,在線程結束時候銷毀。
三:RunLoop相關類
Core Foundation中關於RunLoop的5個類
1:CFRunLoopRef
2:CFRunLoopModeRef ,CFRunLoopModeRef代表RunLoop的運行模式,一個RunLoop包含若干個運行模式,即一個RunLoop包含若干個Mode,每個Mode又包含若干個Source/Timer/Observer,每次RunLoop啟動時,只能指定其中一個Mode,這個Mode被稱作CurrentMode,如果需要切換Mode,只能退出Loop,再重新指定一個Mode進入。系統默認注冊了5個Mode:
(1)kCFRunLoopDefaultMode:App的默認的Mode,通常線程是在這個Mode下運行
(2)UITrackingRunLoopMode:界面跟蹤Mode,用於ScrollView追蹤觸摸滑動,保證界面滑動時不受其它Mode影響
(3)UIInitializationRunLoopMode:在剛啟動App時進入的第一個Mode,啟動完成後就不再使用
(4)GSEventReceiveRunLoopMode:接收系統事件的內部Mode,通常用不到。
(5)kCFRunLoopCommonMode:這是一種占位用的Mode,不是一種真正的Mode。
3:CFRunLoopTimerRef
(1)CFRunLoopTimerRef是基於時間的觸發器。
(2)基本上說的就是NSTimer,它會受到runloop的Mode的影響。
(3)GCD的定時器不受RunLoop的mode的影響。
關於定時器和runloopMode的關系如下代碼
//
// ViewController.m
// CFRunLoopTimerRef
//
// Created by fe on 2016/10/25.
// Copyright © 2016年 fe. All rights reserved.
//
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic , strong) dispatch_source_t timer;
@end
@implementation ViewController
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self GCDTimer];
}
-(void)timer1
{
/*
第一種定時器用法,這種方式創建的定時器,
系統默認添加到NSRunLoop中的NSDefaultRunLoopMode模式中,
但是當用戶和應用交互發生觸摸滑動等事件時,
RunLoop的模式會切換到UITrackingRunLoopMode,
此時定時器就不再工作,因為定時器只在,默認被添加到的NSDefaultRunLoopMode
模式下工作。
*/
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(go) userInfo:nil repeats:YES];
}
-(void)timer2
{
/*
第二種定時器用法,這種方式創建的定時器,如果添加到NSRunLoop,
並設置RunLoop模式為NSDefaultRunLoopMode,
當用戶和應用交互發生觸摸滑動等事件時,
RunLoop的模式會切換到UITrackingRunLoopMode,
此時定時器就不再工作,因為定時器只在NSDefaultRunLoopMode模式下工作。
如果把用這種方式創建的定時器,添加到NSRunLoop,
並設置RunLoop模式為UITrackingRunLoopMode,
在這種情況下,只有當用戶和應用交互發生觸摸滑動等事件時,
定時器才會工作。
為了解決以上兩種RunLoop運行模式造成的定時器,定時不准的問題。
我們可以把定時器添加到RunLoop並設置運行模式為NSRunLoopCommonModes.
在這種運行模式下,不管是默認狀態,還是當用戶和應用交互發生觸摸滑動等事件時。
定時器都可以正常工作。
*/
//1:創建定時器
NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(go) userInfo:nil repeats:YES];
//2.1:把定時器添加到RunLoop中,定時器在NSDefaultRunLoopMode模式下工作。
//[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
//2.2:把定時器添加到RunLoop中,定時器在UITrackingRunLoopMode模式下工作。
//[[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
//2.3
/*
把定時器添加到RunLoop中,設置模式為NSRunLoopCommonModes,
定時器在UITrackingRunLoopMode模式和NSDefaultRunLoopMode模式下都工作。
NSRunLoopCommonModes是一種標記模式,被打上這種標記的模式有以下兩種
0 : contents = "UITrackingRunLoopMode"
2 : contents = "kCFRunLoopDefaultMode"
*/
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
NSLog(@"---------%@",[NSRunLoop currentRunLoop]);
}
-(void)GCDTimer
{
/*
使用GCD的定時器,不會受到RunloopMode的影響。
*/
//0:創建隊列
dispatch_queue_t queue = dispatch_queue_create("cn.520.www", DISPATCH_QUEUE_CONCURRENT);
//1:創建一個GCD定時器
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
self.timer = timer;
//2:設置定時器的開始時間,間隔時間,精確度。精准度一般填0,表示沒有誤差。
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
//3:定時器要調用的方法。
dispatch_source_set_event_handler(timer, ^{
NSLog(@"----------%@",[NSThread currentThread]);
});
//4:啟動定時器。
dispatch_resume(timer);
}
-(void)go
{
NSLog(@"-----------");
}
@end
4:CFRunLoopSourceRef
(1)CFRunLoopSourceRef是事件源(輸入源),分為兩種。
(2)Source0:非基於Port的,用於用戶主動觸發的事件。
(3)Source1:基於Port的,通過內核和其它線程相互發送消息。
5:CFRunLoopObserverRef
(1)CFRunLoopObserverRef是觀察者,能夠監聽RunLoop狀態的改變。
(2)可以監聽的時間點有一下幾個
/* Run Loop Observer Activities */
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),//即將推出RunLoop
kCFRunLoopAllActivities = 0x0FFFFFFFU//監聽所有狀態
};
和CFRunLoopObserverRef相關的代碼如下:
-(void)observer
{
/*
第一個參數:開辟內存空間
第二個參數:監聽runloop的什麼事件
第三個參數:是否持續監聽
第四個參數:優先級
第五個參數:回掉
*/
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
switch (activity){
case kCFRunLoopEntry:
NSLog(@"--runloop即將進入循環--");
break;
case kCFRunLoopBeforeTimers:
NSLog(@"--runloop將要處理timer--");
break;
case kCFRunLoopBeforeSources:
NSLog(@"--runloop將要處理sources--");
break;
case kCFRunLoopBeforeWaiting:
NSLog(@"--runloop將要進入休眠--");
break;
case kCFRunLoopAfterWaiting:
NSLog(@"--runloop休眠結束即將進入循環--");
break;
case kCFRunLoopExit:
NSLog(@"--runloop退出循環--");
break;
default:
break;
}
});
/*
第一個參數:要監聽哪一個runloop
第二個參數:監聽者
第三個參數:要監聽runloop在哪種運行模式下的狀態
*/
CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
}