首先什么是runloop?
顾名思义,运行循环,在程序运行中做一些事情。下面就是一个runloop,让程序不会马上退出,保持运行状态
int main(int argc, char * argv[]) {
NSString * appDelegateClassName;
@autoreleasepool {
// Setup code that might create autoreleased objects goes here.
appDelegateClassName = NSStringFromClass([AppDelegate class]);
}
return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}
RunLoop的基本作用:
- 保持程序的持续运行
- 处理App中的各种事件(比如触摸事件、定时器事件等)
- 节省CPU资源,提高程序性能:该做事时做事,该休息时休息
iOS中有2套API来访问和使用RunLoop,Foundation中的NSRunLoop和Core Foundation的CFRunLoopRef,NSRunLoop是基于CFRunLoopRef的一层OC包装,CFRunLoopRef是开源的,https://opensource.apple.com/tarballs/CF/在这里可以查看。
RunLoop与线程的关系
- 每条线程都有唯一的一个与之对应的RunLoop对象
- RunLoop保存在一个全局的Dictionary里,线程作为key,RunLoop作为value
- 线程刚创建时并没有RunLoop对象,RunLoop会在第一次获取它时创建
- RunLoop会在线程结束时销毁
- 主线程的RunLoop已经自动获取(创建),子线程默认没有开启RunLoop
Core Foundation中关于RunLoop的5个类
- CFRunLoopRef
- CFRunLoopModeRef
- CFRunLoopSourceRef
- CFRunLoopTimerRef
- CFRunLoopObserverRef
- CFRunLoopModeRef代表RunLoop的运行模式
- 一个RunLoop包含若干个Mode,每个Mode又包含若干个Source0/Source1/Timer/Observer
- RunLoop启动时只能选择其中一个Mode,作为currentMode
- 如果需要切换Mode,只能退出当前Loop,再重新选择一个Mode进入
- 如果Mode里没有任何Source0/Source1/Timer/Observer,RunLoop会立马退出
常见的两种model,kCFRunLoopDefaultMode(NSDefaultRunLoopMode):App的默认Mode,通常主线程是在这个Mode下运行,UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
RunLoop的运行逻辑
- 通知Observers:进入Loop
- 通知Observers:即将处理Timers
- 通知Observers:即将处理Sources
- 处理Blocks
- 处理Source0(可能会再次处理Blocks)
- 如果存在Source1,就跳转到第8步
- 通知Observers:开始休眠(等待消息唤醒)
- 通知Observers:结束休眠(被某个消息唤醒) 处理Timer 或者处理GCD Async To Main Queue 或者 处理Source1
- 处理Blocks
- 根据前面的执行结果,决定如何操作: 回到第02步 或者 退出Loop
- 通知Observers:退出Loop
runloop运行原理:是从用户态转到了内核态,在内核态等待消息,有消息就唤醒线程,没有就让线程休眠。
下面是runloop一个实际应用,线程保活,为了方便看是否销毁了线程,写了个FGThread继承了NSThread,实际上直接nsthread就可以了。
@interface FGPermenantThread : NSObject
- (void)executeWith:(void (^)(void))task;
- (void)stop;
@end
@interface FGThread : NSThread
@end
@implementation FGThread
- (void)dealloc{
NSLog(@"%s",__func__);
}
@end
@interface FGPermenantThread ()
@property (strong, nonatomic) FGThread *thread;
@property (assign ,nonatomic) BOOL isStopped;
@end
@implementation FGPermenantThread
- (instancetype)init{
if(self = [super init]){
self.isStopped = NO;
__weak typeof(self) weakSelf = self;
self.thread = [[FGThread alloc] initWithBlock:^{
NSLog(@"----begin");
[[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
if(weakSelf.isStopped && weakSelf){
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
NSLog(@"----end");
}];
[self.thread start];
}
return self;
}
- (void)dealloc{
NSLog(@"%s",__func__);
[self stop];
}
#pragma mark -- public methods
- (void)executeWith:(void (^)(void))task{
if(!self.thread || !task) return;
[self performSelector:@selector(__execute:) onThread:self.thread withObject:task waitUntilDone:NO];
}
- (void)stop{
if(!self.thread) return;
[self performSelector:@selector(__stop) onThread:self.thread withObject:nil waitUntilDone:YES];
}
#pragma mark -- private methods
- (void)__stop{
self.isStopped = YES;
CFRunLoopStop(CFRunLoopGetCurrent());
self.thread = nil;
}
- (void)__execute:(void (^)(void))task{
task();
}
@end