一、概念
runloop
1、字面理解:“跑圈”“循环执行”
2、实义:通过内部维护的事件循环来对事件/消息进行管理的一个对象
** 寄生于线程的消息循环机制,保证线程的存活,而不是线程执行完任务后就消亡
** 没有消息处理->休眠以避免资源占用(用户态->内核态)
** 有消息处理->立即唤醒(内核态->用户态)
二、整体结构
1.一个runloop对应了多种mode,每个mode下又有多种source,timer,Observer
2.每次RunLoop启动时,只能指定其中一个 Mode,这个Mode被称作 CurrentMode
3.如果需要切换Mode,只能退出Loop,再重新指定一个Mode进入,这样做主要是为了分隔开不同组的Source/Timer/Observer,让其互不影响
整体图
三、Runloop运行逻辑
事件队列事件循环的实现机制是
当你被问到,runloop具体操作流程的时候,你该怎么回答。
当我们手动点击屏幕,UI操作,实际runloop是这样来的
事件循环的实现机制
四、主线程默认开启runloop,子线程是不开启RunLoop的!!!子线程的代码执行完毕后就会被回收!!!上代码
@interface ViewController ()
@property (nonatomic,assign,getter=isfinish) BOOL finish;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
_finish = YES;
//子线程
dispatch_async(dispatch_get_global_queue(0, 0), ^{
//初始化NSTimer
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timeup) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
while (self.finish) {
//开启运行循环
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceReferenceDate:0.1]];
}
NSLog(@"当RunLoop停止的时候线程会被销毁");
NSLog(@"%@",[NSThread currentThread]);
});
}
- (void)timeup {
NSLog(@"timer%@",[NSThread currentThread]);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
_finish = NO;
}
运行结果 可正常执行子线程
2020-05-09 17:36:58.740731+0800 demoTest[86561:2515635] timer<NSThread: 0x600002137400>{number = 3, name = (null)}
2020-05-09 17:36:59.744646+0800 demoTest[86561:2515635] timer<NSThread: 0x600002137400>{number = 3, name = (null)}
2020-05-09 17:37:00.743766+0800 demoTest[86561:2515635] timer<NSThread: 0x600002137400>{number = 3, name = (null)}
2020-05-09 17:37:01.743925+0800 demoTest[86561:2515635] timer<NSThread: 0x600002137400>{number = 3, name = (null)}
2020-05-09 17:37:02.740088+0800 demoTest[86561:2515635] timer<NSThread: 0x600002137400>{number = 3, name = (null)}
点击屏幕任何地方后运行结果:线程被销毁,即不再执行运行循环
//开启运行循环
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceReferenceDate:0.1]];
2020-05-09 17:36:59.744646+0800 demoTest[86561:2515635] timer<NSThread: 0x600002137400>{number = 3, name = (null)}
2020-05-09 17:37:00.743766+0800 demoTest[86561:2515635] timer<NSThread: 0x600002137400>{number = 3, name = (null)}
2020-05-09 17:37:01.743925+0800 demoTest[86561:2515635] timer<NSThread: 0x600002137400>{number = 3, name = (null)}
2020-05-09 17:37:02.740088+0800 demoTest[86561:2515635] timer<NSThread: 0x600002137400>{number = 3, name = (null)}
2020-05-09 17:37:03.189652+0800 demoTest[86561:2515635] 当RunLoop停止的时候线程会被销毁
2020-05-09 17:37:03.189834+0800 demoTest[86561:2515635] <NSThread: 0x600002137400>{number = 3, name = (null)}
小结:主线程默认开启runloop,子线程不开启RunLoop,需要代码添加执行循环
五、实现一个常驻线程
如何实现一个常驻线程,又怎么销毁一个常驻线程。
说明:为什么要实现常驻线程,因为有些操作,需要多次在子线程中执行,但是我们不想每次都开启执行一次,结束了。。多处使用场景,让子线程runloop一直存在,节省内存开销。
三种方法实现常驻线程
创建子线程
@interface ViewController ()
@property(nonatomic,strong)NSThread * testThread;
@property(nonatomic,assign)BOOL runAlways;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.testThread = [[NSThread alloc] initWithTarget:self selector:@selector(runTask) object:nil];
self.testThread.name = @"测试线程";
[self.testThread start];
}
实现常驻线程
1、addPort
2、addTimer
3、CFRunLoopAddSource
-(void)runTask{
self.runAlways = YES;
NSLog(@"执行线程:%@",[NSThread currentThread]);
//--------第一种 addPort
//获取当前线程的runLoop
NSRunLoop* runLoop = [NSRunLoop currentRunLoop];
//为当前的runLoop添加model
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
//运行 runLoop
[runLoop run];
//----------第二种 addTimer
/*
//获取当前线程的runLoop
NSRunLoop* runLoop = [NSRunLoop currentRunLoop];
NSTimer * timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(runTask2) userInfo:nil repeats:YES];
[runLoop addTimer:timer forMode:NSRunLoopCommonModes];
[runLoop run];
*/
//----------第三种source
/*
//创建source
CFRunLoopSourceContext context = {0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
CFRunLoopSourceRef source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
//创建runloop同时在DefaultMode模式下添加source
CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
while (self.runAlways) {
NSLog(@"source--当前线程:%@",[NSThread currentThread]);
CFRunLoopRun();
}
//某一时机 runAlways为no时,保证跳出runloop,线程退出
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
CFRelease(source);
*/
}
-(void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[self performSelector:@selector(runTask2) onThread:self.testThread withObject:nil waitUntilDone:NO];
}
-(void)runTask2{
NSLog(@"当前线程:%@",[NSThread currentThread]);
NSLog(@"%s 完成",__PRETTY_FUNCTION__);
}
1、addPort 运行结果 实现常驻,触发页面时持有线程
2020-05-10 11:18:04.837590+0800 demoOne[33638:1500106] 执行线程:<NSThread: 0x600001e8b540>{number = 5, name = 测试线程}
2020-05-10 11:18:06.616884+0800 demoOne[33638:1500106] 当前线程:<NSThread: 0x600001e8b540>{number = 5, name = 测试线程}
2020-05-10 11:18:06.617043+0800 demoOne[33638:1500106] -[ViewController runTask2] 完成
2、addTimer 运行结果 实现常驻,每隔1.0秒走一次方法
2020-05-10 11:28:00.349596+0800 demoOne[33668:1505508] 执行线程:<NSThread: 0x60000089ec00>{number = 6, name = 测试线程}
2020-05-10 11:28:01.352583+0800 demoOne[33668:1505508] 当前线程:<NSThread: 0x60000089ec00>{number = 6, name = 测试线程}
2020-05-10 11:28:01.352864+0800 demoOne[33668:1505508] -[ViewController runTask2] 完成
2020-05-10 11:28:01.436690+0800 demoOne[33668:1505508] 当前线程:<NSThread: 0x60000089ec00>{number = 6, name = 测试线程}
2020-05-10 11:28:01.436875+0800 demoOne[33668:1505508] -[ViewController runTask2] 完成
2020-05-10 11:28:02.352970+0800 demoOne[33668:1505508] 当前线程:<NSThread: 0x60000089ec00>{number = 6, name = 测试线程}
2020-05-10 11:28:02.353353+0800 demoOne[33668:1505508] -[ViewController runTask2] 完成
2020-05-10 11:28:03.352095+0800 demoOne[33668:1505508] 当前线程:<NSThread: 0x60000089ec00>{number = 6, name = 测试线程}
2020-05-10 11:28:03.352318+0800 demoOne[33668:1505508] -[ViewController runTask2] 完成
3、CFRunLoopAddSource 运行结果 实现常驻,触发页面时持有线程
2020-05-10 11:30:03.492318+0800 demoOne[33706:1507948] 执行线程:<NSThread: 0x60000043a840>{number = 7, name = 测试线程}
2020-05-10 11:30:03.492603+0800 demoOne[33706:1507948] source--当前线程:<NSThread: 0x60000043a840>{number = 7, name = 测试线程}
2020-05-10 11:30:05.379058+0800 demoOne[33706:1507948] 当前线程:<NSThread: 0x60000043a840>{number = 7, name = 测试线程}
2020-05-10 11:30:05.379283+0800 demoOne[33706:1507948] -[ViewController runTask2] 完成
source 退出子线程操作 添加限制条件
- (void)createButton{
UIButton * btn = [UIButton buttonWithType:UIButtonTypeCustom];
btn.frame = CGRectMake(100, 100, 100, 100);
btn.backgroundColor = [UIColor lightGrayColor];
[btn setTitle:@"按钮" forState:UIControlStateNormal];
[btn addTarget:self action:@selector(click) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:btn];
}
- (void)click{
self.runAlways = NO;
NSLog(@"点击按钮source移除----当前线程:%@",[NSThread currentThread]);
}
运行结果:当前线程已在主线程,点击屏幕又会持有子线程
2020-05-10 11:53:41.415894+0800 demoOne[33924:1525077] 执行线程:<NSThread: 0x600001b57740>{number = 7, name = 测试线程}
2020-05-10 11:53:43.843947+0800 demoOne[33924:1525077] source--当前线程:<NSThread: 0x600001b57740>{number = 7, name = 测试线程}
2020-05-10 11:53:46.977248+0800 demoOne[33924:1525077] 当前线程:<NSThread: 0x600001b57740>{number = 7, name = 测试线程}
2020-05-10 11:53:46.977501+0800 demoOne[33924:1525077] -[ViewController runTask2] 完成
2020-05-10 11:53:50.492606+0800 demoOne[33924:1524988] 点击按钮source移除----当前线程:<NSThread: 0x600001b18ec0>{number = 1, name = main}
六、具体实例
CFRunloopSourceRef
1、Source0:非基于Port的 ,用于用户主动触发事件
2、Source1:基于Port的,通过内核和其它线程相互发送消息
可以通过打断点的方式查看一个方法的函数调用栈
函数调用栈
AFNetworking 中运用 Runloop
AFURLConnectionOperation 基于 NSURLConnection 构建,其希望能在后台线程接收 Delegate 回调。为此 AFNetworking 单独创建了一个线程,并在这个线程中启动了一个 RunLoop
+ (void)networkRequestThreadEntryPoint:(id)__unused object {
@autoreleasepool {
[[NSThread currentThread] setName:@"AFNetworking"];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
[runLoop run];
}
}
+ (NSThread *)networkRequestThread {
static NSThread *_networkRequestThread = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];
[_networkRequestThread start];
});
return _networkRequestThread;
}
此处添加 port 只是为了让 RunLoop 不至于退出,并没有用于实际的发送消息
当需要这个后台线程执行任务时,AFNetworking 通过调用 [NSObject performSelector:onThread:..] 将这个任务扔到了后台线程的 RunLoop 中实现消息发送
- (void)start {
[self.lock lock];
if ([self isCancelled]) {
[self performSelector:@selector(cancelConnection) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
} else if ([self isReady]) {
self.state = AFOperationExecutingState;
[self performSelector:@selector(operationDidStart) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
}
[self.lock unlock];
}
timer
三种创建方式
1、第一种创建方式timer(系统会默认添加到Runloop的NSDefaultRunLoopMode中)
1),scheduledTimerWithTimeInterval: invocation: repeats:
2),scheduledTimerWithTimeInterval: target: selector: userInfo: repeats:
//初始化NSTimer---1主线程直接
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timeup) userInfo:nil repeats:YES];
- (void)timeup {
NSLog(@"时间到%@",[NSThread currentThread]);
}
运行结果主线程直接启动不需要代码添加到runloop
2020-05-09 16:50:27.279293+0800 demoTest[85568:2475995] 时间到<NSThread: 0x600002a06880>{number = 1, name = main}
2020-05-09 16:50:28.278690+0800 demoTest[85568:2475995] 时间到<NSThread: 0x600002a06880>{number = 1, name = main}
2020-05-09 16:50:29.279113+0800 demoTest[85568:2475995] 时间到<NSThread: 0x600002a06880>{number = 1, name = main}
2020-05-09 16:50:30.278079+0800 demoTest[85568:2475995] 时间到<NSThread: 0x600002a06880>{number = 1, name = main}
2020-05-09 16:50:31.279187+0800 demoTest[85568:2475995] 时间到<NSThread: 0x600002a06880>{number = 1, name = main}
2、第二种创建方式timer(代码添加到runloop)
1),timerWithTimeInterval: target: selector: userInfo: repeats:
2),timerWithTimeInterval: invocation: repeats:
//初始化NSTimer---2
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timeup) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
- (void)timeup {
NSLog(@"NSTimer---2 %@",[NSThread currentThread]);
}
运行结果
2020-05-09 16:55:57.607139+0800 demoTest[85709:2481598] NSTimer---2 <NSThread: 0x6000036e2900>{number = 1, name = main}
2020-05-09 16:55:58.606587+0800 demoTest[85709:2481598] NSTimer---2 <NSThread: 0x6000036e2900>{number = 1, name = main}
2020-05-09 16:55:59.606793+0800 demoTest[85709:2481598] NSTimer---2 <NSThread: 0x6000036e2900>{number = 1, name = main}
2020-05-09 16:56:00.606252+0800 demoTest[85709:2481598] NSTimer---2 <NSThread: 0x6000036e2900>{number = 1, name = main}
2020-05-09 16:56:01.606854+0800 demoTest[85709:2481598] NSTimer---2 <NSThread: 0x6000036e2900>{number = 1, name = main}
3、第三种创建方式timer(代码添加到runloop)
initWithFireDate: interval: target: selector: userInfo: repeats:
//初始化NSTimer---3
NSTimer *timer= [[NSTimer alloc]initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:5]interval:1 target:self selector:@selector(timeup) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
- (void)timeup {
NSLog(@"NSTimer---3 %@",[NSThread currentThread]);
}
运行结果
2020-05-09 16:58:47.493477+0800 demoTest[85782:2484896] NSTimer---3 <NSThread: 0x6000012bc240>{number = 1, name = main}
2020-05-09 16:58:48.493312+0800 demoTest[85782:2484896] NSTimer---3 <NSThread: 0x6000012bc240>{number = 1, name = main}
2020-05-09 16:58:49.493144+0800 demoTest[85782:2484896] NSTimer---3 <NSThread: 0x6000012bc240>{number = 1, name = main}
CFRunLoopObserverRef 观察者,能够监听RunLoop的状态改变
NSLog(@"当前线程:%@",[NSThread currentThread]);
//创建一个runloop监听者
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(),kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
NSLog(@"监听runloop状态改变---%zd",activity);
});
//为runloop添加一个监听者
CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
CFRelease(observer);
运行结果:能runloop状态
2020-05-10 17:33:08.727344+0800 demoOne[35093:1679140] 当前线程:<NSThread: 0x600002ae8240>{number = 1, name = main}
2020-05-10 17:33:39.321042+0800 demoOne[35093:1679140] 监听runloop状态改变---2
2020-05-10 17:33:39.321231+0800 demoOne[35093:1679140] 监听runloop状态改变---4
2020-05-10 17:33:39.322211+0800 demoOne[35093:1679140] 监听runloop状态改变---2
2020-05-10 17:33:39.322354+0800 demoOne[35093:1679140] 监听runloop状态改变---4
2020-05-10 17:33:39.324036+0800 demoOne[35093:1679140] 监听runloop状态改变---2
2020-05-10 17:33:39.324189+0800 demoOne[35093:1679140] 监听runloop状态改变---4
2020-05-10 17:33:39.324511+0800 demoOne[35093:1679140] 监听runloop状态改变---2
2020-05-10 17:33:39.324640+0800 demoOne[35093:1679140] 监听runloop状态改变---4
2020-05-10 17:33:39.337857+0800 demoOne[35093:1679140] 监听runloop状态改变---2
2020-05-10 17:33:39.338037+0800 demoOne[35093:1679140] 监听runloop状态改变---4
2020-05-10 17:33:39.338532+0800 demoOne[35093:1679140] 监听runloop状态改变---2
具体常用事例:tableview中渲染多张图片出现卡顿问题,可用runloop循环使用观察者每次只渲染几张大图解决卡顿
OB实例(加载1024x1024大图)
typedef void(^runloopBlock) (void);
@interface ViewController ()<UITableViewDelegate,UITableViewDataSource>
@property (nonatomic,strong) UITableView * tableView;
@property (nonatomic,strong) NSMutableArray * taskes;
@property (nonatomic,assign) NSInteger maxQueueLength;
@end
static NSString * ID = @"IDCELL";
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//让runloop不休眠(一直存活,一直走"唤醒")runloop添加了timer 瞬间观察到runloop
[NSTimer scheduledTimerWithTimeInterval:0.001 target:self selector:@selector(timeMethond) userInfo:nil repeats:YES];
_taskes = [[NSMutableArray alloc]init];
_maxQueueLength = 9 ;
//添加观察者
[self addRunloopObserver];
[self.view addSubview:self.tableView];
}
- (void)timeMethond{
//不做任何事只为runloop唤醒
}
- (UITableView *)tableView{
_tableView=[[UITableView alloc]initWithFrame:self.view.bounds style:UITableViewStyleGrouped];
[_tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:ID];
_tableView.delegate=self;
_tableView.dataSource=self;
return _tableView;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return 140;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return 100;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 1;
}
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:ID];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
for (NSInteger i = 1; i <= 4; i++) {
[[cell.contentView viewWithTag:i] removeFromSuperview];
//优化卡顿
[self addTaskes:^{
[ViewController addImageWith:cell index:i];
}];
// //卡顿
// [ViewController addImageWith:cell index:i];
}
return cell;
}
#pragma mark 关于Runloop C语言
- (void)addRunloopObserver{
//获取runloop
CFRunLoopRef runloop= CFRunLoopGetCurrent();
//初始化runloop观察者上下文结构体
CFRunLoopObserverContext context = {0,(__bridge void *)(self),&CFRetain,&CFRelease,NULL};
//定义观察者
static CFRunLoopObserverRef defaultObserver;
defaultObserver = CFRunLoopObserverCreate(NULL, kCFRunLoopBeforeWaiting, YES, NSIntegerMax-999, &callBack, &context);
//观察添加到当前runloop
CFRunLoopAddObserver(runloop, defaultObserver, kCFRunLoopCommonModes);
CFRelease(defaultObserver);
}
//runloop中事件的执行体
static void callBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity,void * info){
NSLog(@"唤醒");
ViewController * VC = (__bridge ViewController *)info;
if (VC.taskes.count==0) {
return;
}
//取出任务
runloopBlock block = VC.taskes.firstObject;
block();
[VC.taskes removeObjectAtIndex:0];
}
//添加block 任务
- (void)addTaskes:(runloopBlock)task{
[self.taskes addObject:task];
if (self.taskes.count > _maxQueueLength) {
[self.taskes removeObjectAtIndex:0];
}
}
+ (void)addImageWith:(UITableViewCell *)cell index:(NSInteger)index{
UIImageView * imageV = [[UIImageView alloc]initWithFrame:CGRectMake(20*index+80*(index-1),5 , 80, 90)];
imageV.tag = index;
NSString * path = [[NSBundle mainBundle] pathForResource:@"imagetest" ofType:@"png"];
UIImage * image = [UIImage imageWithContentsOfFile:path];
imageV.image = image;
[UIView transitionWithView:cell.contentView duration:0.3 options:(UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionTransitionCrossDissolve) animations:^{
[cell.contentView addSubview:imageV];
} completion:nil];
}
1024X1024大图
七、补充
定时器:NSTimer 与GCD
NSTimer创建
@property(nonatomic,strong)NSTimer* timer;
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timeup) userInfo:nil repeats:YES];
- (void)timeup {
NSLog(@"时间到%@",[NSThread currentThread]);
}
运行结果
2020-05-10 16:46:28.031652+0800 demoOne[34828:1650155] 时间到<NSThread: 0x600002fec380>{number = 1, name = main}
2020-05-10 16:46:29.032614+0800 demoOne[34828:1650155] 时间到<NSThread: 0x600002fec380>{number = 1, name = main}
取消timer
[self.timer invalidate];
self.timer = nil;
GCD timer创建
@property(nonatomic,strong)dispatch_source_t gcdTimer;
//0.创建一个队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//1.创建一个GCD的定时器
/*
第一个参数:说明这是一个定时器
第四个参数:GCD的回调任务添加到那个队列中执行,如果是主队列则在主线程执行
*/
dispatch_source_t gcdTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
//2.设置定时器的开始时间,间隔时间以及精准度
//设置开始时间,三秒钟之后调用,注:GCD中的时间为纳秒NSEC_PER_SEC,3.0 *NSEC_PER_SEC即为3秒
dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW,3.0 *NSEC_PER_SEC);
//设置定时器工作的间隔时间
uint64_t intevel = 1.0 * NSEC_PER_SEC;
/*
第一个参数:要给哪个定时器设置
第二个参数:定时器的开始时间DISPATCH_TIME_NOW表示从当前开始
第三个参数:定时器调用方法的间隔时间
第四个参数:定时器的精准度,如果传0则表示采用最精准的方式计算,如果传大于0的数值,则表示该定时切换可以接收该值范围内的误差,通常传0
该参数的意义:可以适当的提高程序的性能
注意点:GCD定时器中的时间以纳秒为单位(面试)
*/
dispatch_source_set_timer(gcdTimer, start, intevel, 0 * NSEC_PER_SEC);
//3.设置定时器开启后回调的方法
/*
第一个参数:要给哪个定时器设置
第二个参数:回调block
*/
dispatch_source_set_event_handler(gcdTimer, ^{
NSLog(@"------%@",[NSThread currentThread]);
});
//4.执行定时器
dispatch_resume(gcdTimer);
//注意:dispatch_source_t本质上是OC类,在这里是个局部变量,需要强引用
self.gcdTimer = gcdTimer;
运行结果:在子线程
2020-05-10 16:49:08.220418+0800 demoOne[34883:1653302] ------<NSThread: 0x600003492ec0>{number = 7, name = (null)}
2020-05-10 16:49:09.219542+0800 demoOne[34883:1653302] ------<NSThread: 0x600003492ec0>{number = 7, name = (null)}
2020-05-10 16:49:10.219824+0800 demoOne[34883:1653302] ------<NSThread: 0x600003492ec0>{number = 7, name = (null)}
2020-05-10 16:49:11.220617+0800 demoOne[34883:1653309] ------<NSThread: 0x6000034fd940>{number = 5, name = (null)}
取消定时器
// 演示如何取消定时器
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 取消定时器
dispatch_cancel(self.gcdTimer);
});
异同点
NSTimer:
1、NSTimer是RunLoop的源
2、自定义的需要加入mode
3、 必须调用 invalidate 来停止其定时任务,并且NSTimer 对其Target是强引用,要注意Target 与 - NSTimer间造成的循环引用造成的内存泄漏
4、不准时(原因:1.RunLoop循环处理的时间,可能某个时刻runloop需要处理很多任务;2.受RunLoop模式的影响,如果NSTimer没有加入到NSRunLoopCommonModes的话,就会受到UITrackingRunLoopMode和NSDefaultRunLoopMode的切换影响)
GCD的timer
1、gcd的timer是dispatch的源
2、不需要加入mode,GCD内部已经将runloop进行封装
3、精度很高
持续更新~
网友评论