运行循环(消息循环)和自动释放池的关系图解
运行循环在 iOS 开发中几乎不用,但是概念的理解却非常重要。
自动释放池的创建和销毁
运行循环检测到事件并启动后,就会自动创建自动释放池.一次完整的运行循环结束之前,自动释放池就会被销毁.作用:
保证程序不退出。
负责监听所有事件,例如:手势触摸,时钟触发,网络加载数据完成等。特性:
没有事件时,会休眠(省电),一旦监听到事件,会立即响应。
每一个线程都有一个 runloop,但是只有主线程的 runloop 会默认启动。子线程的运行循环默认是不启动的。
//经典面试题:
long largeNumber = 1000000;
// for (int i = 0; i < largeNumber; ++i) {
// // 相当于创建一次就销毁一次,保证了内存平衡
// NSString *str = @"Hello World";
// str = [str stringByAppendingFormat:@" - %d", i];
// str = [str uppercaseString];
// }
/*
1.内存瞬间暴涨
2.因为for循环的速度非常快,CPU来不及处理内存
3.解决办法 : 在for循环的开始就创建自动释放池
4.原理 : 出了自动释放池的作用域里面的对象就销毁了
*/
for (int i = 0; i < largeNumber; ++i) {
// 相当于创建一次就销毁一次,保证了内存平衡
@autoreleasepool {
NSString *str = @"Hello World";
str = [str stringByAppendingFormat:@" - %d", i];
str = [str uppercaseString];
}
}
主线程中使用 NSTimer
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self timerDemo];
}
// 主线程的消息循环默认开启的
#pragma mark - 主线程中使用 NSTimer
- (void)timerDemo {
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(fireDemo) userInfo:nil repeats:YES];
// 获取当前主线程的运行循环,把定时器添加进去
// currentRunLoop : 获取当前主线程的运行循环
// forMode : 是timer的运行模式
// 运行循环也是要运行在一定的模式下的.这个模式默认就是kCFRunLoopDefaultMode.只有运行循环的模式和timer的运行模式保持一致,那么运行循环才能够检测到timer的事件
// 提示 : 不同的UI交互,运行循环的模式会自动的发生变化
// NSRunLoopCommonModes : 模式组,内部包含有多种模式,包含kCFRunLoopDefaultMode和UITrackingRunLoopMode
// UITrackingRunLoopMode : 追踪模式,是苹果专门设计的为滚动视图准备的
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
// NSLog(@"%@",[NSRunLoop currentRunLoop].currentMode);
}
- (void)fireDemo {
// currentMode : 当前运行循环的模式
NSLog(@"hello %@",[NSRunLoop currentRunLoop].currentMode);
}
子线程中使用 NSTimer
- 子线程运行循环:
子线程的运行循环默认是不启动的。
启动运行循环后,如果不停止运行循环,不会执行后续的任何代码,会形成一个死循环。
一旦停止了运行循环,后续代码能够执行,执行完毕后,线程被自动销毁。
// 子线程里面的消息循环默认不开启,因为子线程不需要处理UI事件
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[self performSelectorInBackground:@selector(timerDemo) withObject:nil];
}
//在子线程中使用NSTimer
- (void)timerDemo {
// 创建定时器
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(fireDemo) userInfo:nil repeats:YES];
// 把定时器添加到当前的线程的运行循环
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
// 需要手动的开启当前子线程的运行循环,这种开启的方式是个无法执行完的死循环
// [[NSRunLoop currentRunLoop] run];
//设置一个时限,让循环结束
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:3.0]];
// 提问 : mark神马时候打印? 不会打印的,因为currentRunLoop是个死循环
NSLog(@"mark");
}
- (void)fireDemo { NSLog(@"hello"); }