什么是RunLoop
简单的来说就是运行循环、跑圈。
- 保持着程序的持续运行并接受用户输入
- 处理APP中的各种事件(触摸,定时器,Selector)
- 节省了CPU资源,提高程序性能,即该做事时做事,该休息时休息。
RunLoop对象
iOS中有2套API来访问RunLoop
- Foundation
NSRunloop
- Core Foundation
CFRunLoopRef
CFRunLoopRef
是用C语言写的更底层一些。NSRunLoop
其实是对CFRunLoopRef
的一个简单封装,CFRunLoopRef
的性能高一点
获取RunLoop对象
- NSRunLoop
[NSRunLoop currentRunLoop];获得当前线程的RunLoop对象
[NSRunLoop mainRunLoop];获得主线程的RunLoop对象
- CFRunLoopRef
CFRunLoopGetCurrent();//获得当前线程的RunLoop对象
CFRunLoopGetMain();//获得主线程的RunLoop对象
RunLoop与线程间的关系
- 每条线程都有唯一的一个与之对应的RunLoop对象
- 主线程的RunLoop已经自动创建完成,子线程的需要手动创建
- RunLoop在第一次获取时创建,在线程结束时销毁
CFRunLoopRef
image.png- CFRunLoopRefModeRef
代表RunLoop的运行模式。
kCFRunLoopDefaultMode //app的默认Mode,通常主线程是在这个Mode下运行
UITrackingRunLoopMode //界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
UIInitializationRunLoopMode //在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用
GSEventReceiveRunLoopMode //接受系统事件的内部 Mode,通常用不到
kCFRunLoopCommonModes //这是一个占位用的Mode,不是一种真正的Mode
- CFRunLoopObserverRef
观察者,能够监听RunLoop的状态变化。
kCFRunLoopEntry //即将进入loop
kCFRunLoopBeforeTimers //即将处理timer
kCFRunLoopBeforeSources //即将处理source
kCFRunLoopBeforeWaiting //即将进入休眠
kCFRunLoopAfterWaiting //即将休眠中唤醒
kCFRunLoopExit //即将退出loop
kCFRunLoopAllActivities //所有状态
- CFRunLoopSourceRef
RunLoop要处理的事件源,定义了两种Source
Source0
:处理APP内部时间,APP自己负责管理(触发),如UIevent...基于非Port的
Source1
:由RunLoop和内核管理的系统的一些事件,基于Port的。 - CFRunLoopTimerRef
是基于事件的触发器,即NSTimer
创建一个RunLoop之后,有默认的运行模式mode,也可以为RunLoop指定运行模式,RunLoop启动必须得有运行模式,而且在运行模式中必须还有timer
或是source
事件其中之一,否则RunLoop就会退出。CFRunLoopModeRef
代表RunLoop的运行模式
一个 RunLoop 包含若干个Mode
,每个Mode
又包含若干个Source
、Timer
、Observer
每次RunLoop启动时,只能指定其中一个Mode
,这个Mode
被称作 CurrentMode
如果需要切换Mode
,只能退出RunLoop,再重新指定一个Mode
进入,这样做主要是为了分隔开不同组的Source
、Timer
、Observer
,让其互不影响。
/*
1参:怎么分配存储空间
2参:眼监听的状态 kCFRunLoopAllActivities 所有状态
3参:是否持续监听
4参:优先级 总是传0
5参:当状态改变时候的回调
*/
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
switch (activity) {
case kCFRunLoopEntry:
NSLog(@"即将进入runloop");
break;
case kCFRunLoopBeforeTimers:
NSLog(@"即将处理timer事件");
break;
case kCFRunLoopBeforeSources:
NSLog(@"即将处理source事件");
break;
case kCFRunLoopBeforeWaiting:
NSLog(@"即将进入睡眠");
break;
case kCFRunLoopAfterWaiting:
NSLog(@"被唤醒");
case kCFRunLoopExit:
NSLog(@"runloop退出");
break;
default:
break;
}
});
/*
1参:要监听那个runloop
2参:观察者
3参:运行模式
*/
CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
CFRelease(observer);//移除
NSRunLoop
- NSTimer问题
NSTimer
使用scheduledTimerWithTimeInterval
此方法创建的定时器默认加到了NSRunLoop
中,并且设置运行模式为默认default,但是如果有滚动事件的时候,定时器就会停止工作。
解决办法:更改NSRunLoop
的Mode为NSRunLoopCommonModes
,滚动拖拽页面时定时器仍然工作。
//1.创建定时器
NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
NSRunLoop *currentRunloop = [NSRunLoop currentRunLoop];
//添加至RunLoop并更改运行模式
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
//开启runloop
[currentRunloop run];
- 线程常驻
创建类GYThread
继承NSThread
。GYThread.m
中添加dealloc
。
- (void)dealloc{
NSLog(@"线程被销毁了");
}
初始化线程,并创建该线程的RunLoop,s
_thread = [[GYThread alloc]initWithTarget:self selector:@selector(threadtest) object:nil];
_thread.name = @"geyang";
[_thread start];
- (void)threadtest{
NSLog(@"--%@--", [NSThread currentThread]);
//启动该�线程的runloop
[[NSRunLoop currentRunLoop]addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop]run];
}
//检测线程是否被销毁
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[self performSelector:@selector(threadAgain) onThread:_thread withObject:nil waitUntilDone:NO];
}
- (void)threadAgain{
NSLog(@"---%@---", [NSThread currentThread]);
}
此时,我们可以点击屏幕可看到打印结果
2017-07-13 15:23:24.382 RunLoop与线程[9290:1054512] ---<GYThread: 0x608000269300>{number = 3, name = geyang}---
说明该线程并没有被销毁
网友评论