什么是RunLoop?这是我们经常听到的东西,但他到底是何方神圣呢!!他们我们一起来学习下
概念
运行的循环,一个死循环!!!
作用
- 保证线程不退出,就是保证程序运行。
- 负责监听所有事件(触摸、时间、网络)。
- 提高cpu的利用率。
扩展
iOS里面有两套API可以访问和使用RunLoop:
Foundation
NSRunLoop
Core Foundation
CFRunLoopRef
上面两套都可以使用,CFRunLoopRef使用c语言写的,是开源的,相比于NSRunLoop更加底层,而NSRunLoop其实是对CFRunLoopRef的一个简单的封装。这样说来,显然CFRunLoopRef的性能要高一点。但是下面我们研究是NSRunLoop,CFRunLoopRef暂时不考虑。RunLoop与线程
1.每条线程都有唯一的与之对应的RunLoop对象。
2.主线程的RunLoop已经创建好了,而子线程的需要手动创建。
3.RunLoop在第一次获取时创建,在线程结束时销毁。
RunLoop实例
- main函数,程序入口
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
先来了解下参数,前两个参数是系统传过来的,第四个参数NSStringFromClass([AppDelegate class]),得到的是一个字符串,@“ AppDelegate”,那就可以写成return UIApplicationMain(argc, argv, nil, @“ AppDelegate”);
int main(int argc, char * argv[]) {
@autoreleasepool {
int a = UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
NSLog(@"我来了!");
return a;
}
}
这个地方能不能打印?运行下你会发现怎么都不会打印的!为什么呢?因为程序一起动就创建了一个主线程,主线程中有一个RunLoop,UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));这有一个死循环,就像
while(yes){ };
- Timer
NSRunLoopModel
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerMethod) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
}
- (void)timerMethod
{
NSLog(@"2来这里");
static int i = 0;
NSLog(@"%d", i++);
}
[NSRunLoop currentRunLoop] 是获取当前线程,当前是主线程。
这里NSDefaultRunLoopMode是默认模式,但如果这个时候有个UITableView在滚动的时候,打印将会停下,优先处理UI事件,如果需要继续打印,可以这样写:
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
同时把Timer加入到默认模式和UI模式中,也可以写成这样:
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
咦~我们发现NSDefaultRunLoopMode和UITrackingRunLoopMode一起达到了NSRunLoopCommonModes的效果,这就是NSRunLoopCommonModes——占位模式。
其实还有两种模式,但是我们用不到,不用管。
Timer中不建议处理耗时操作,要放到子线程
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerMethod) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}
- (void)timerMethod
{
[NSThread sleepForTimeInterval:1.0];
static int i = 0;
NSLog(@"%d", i++);
}
上面这段代码中,我们继续拖拽UITableView,这时候你会发现,拖拽的时候回卡顿,这又是为什么呢?
Apple官方也有说明吗,Timer中不建议处理耗时操作,这样就是直观了!
子线程要开启RunLoop
- (void)viewDidLoad {
[super viewDidLoad];
NSThread *thread = [[NSThread alloc] initWithBlock:^{
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerMethod) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
NSLog(@"来这里");
}];
[thread start];
}
- (void)timerMethod
{
NSLog(@"方法这里");
static int i = 0;
NSLog(@"%d", i++);
}
打印结果:
2018-04-02 15:13:17.397698+0800 iOS_RunLoop[3141:155626] 来这里
这时候发现不会走到- (void)timerMethod;方法中,我们可以新建一个继承于NSTread的类,实现- (void)dealloc{ nslog(@"线程死了")},代码来到了这里,线程已经挂掉了,悲剧了!
接下来我们就思考怎样才能线程不挂掉呢?
给它一个强引用?我们来验证下
@property (nonatomic, strong) NSThread *jey_thread;
- (void)viewDidLoad {
[super viewDidLoad];
NSThread *thread = [[NSThread alloc] initWithBlock:^{
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerMethod) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
NSLog(@"来这里");
}];
self.jey_thread = thread;
[thread start];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self.jey_thread start];
}
点击屏幕,发现程序崩溃了!强引用不管用了,为什么?
解释:强引用保住的是NSTread OC对象,线程并没有保住。线程只能通过RunLoop开启来保住。
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSThread *thread = [[NSThread alloc] initWithBlock:^{
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerMethod) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
NSLog(@"来这里");
}];
[thread start];
}
- (void)timerMethod
{
NSLog(@"方法这里");
static int i = 0;
NSLog(@"%d", i++);
}
让当前线程的RunLoop跑起来: [[NSRunLoop currentRunLoop] run];
跑一下代码,发现NSLog(@"来这里");不会被打印,因为 [[NSRunLoop currentRunLoop] run]是个死循环啊!哈哈哈