1.定时器场景
- 通过timer开头的类方法创建定时器时,必须将NSTimer添加到RunLoop中, 才能执行定时器
- 将NSTimer添加到主线程NSRunLoop的默认模式下, 只有主线程NSRunLoop当前是默认模式才会执行timer
- 将NSTimer添加到主线程NSRunLoop的追踪模式,只有主线程NSRunLoop当前是追踪模式才会执行timer
- NSRunLoopCommonModes这是一个占位用的Mode,不是一种真正的Mode,其实Common是一个标识,它是将NSDefaultRunLoopMode和UITrackingRunLoopMode标记为了Common.所以,只要将timer添加到Common占位模式下,timer就可以在NSDefaultRunLoopMode和UITrackingRunLoopMode模式下都能运行
- 使用scheduledTimer类方法创建定时器无需手动添加,系统会自动添加到RunLoop的NSDefaultRunLoopMode模式
- (void)viewDidLoad {
[super viewDidLoad];
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(demo) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}
-(void)demo{
NSLog(@"%s",__func__);
NSLog(@"%@",[NSRunLoop mainRunLoop].currentMode);
}
2.ImageView显示
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(@"%s", __func__);
[self.imageView performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@"def"] afterDelay:2.0 inModes:@[UITrackingRunLoopMode]];
}
3.常驻线程
- 我们知道主线程会一直运行不会退出是因为存在主RunLoop,这时候主线程可以被称之为常驻线程
- 因为默认情况下只要一个线程的任务执行完毕,那么这个线程就不能使用了,有时候我们会频繁的开启新的线程执行一些一些重复的操作(例如音乐播放、视频播放等),这个时候我们可能会需要一个常驻的子线程来执行这些操作,在需要的时候执行操作,不需要的时候休眠,而不用反复的开启新的线程,这个时候我们就需要自己来创建子线程的RunLoop
- (void)viewDidLoad {
[super viewDidLoad];
self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(demo) object:nil];
[self.thread start];
}
-(void)demo{
NSLog(@"%s",__func__);
NSRunLoop *runloop = [NSRunLoop currentRunLoop];
[runloop addPort:[NSMachPort port] forMode:NSRunLoopCommonModes];
[runloop run];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
[self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:YES];
}
-(void)test{
NSLog(@"%s",__func__);
}
-[ViewController demo]
-[ViewController test]
- 注意点:
- currentRunLoop仅仅代表创建了一个NSRunLoop对象, 并没有运行RunLoop
- 一个NSRunLoop中, 如果没有source或者timer, 那么NSRunLoop就会退出死循环
4.自动释放池
- 在程序入口可以看到程序最开始就创建了一个自动循环池,在程序运行过程中,即在RunLoop运行循环过程中会不断的创建和释放自动释放池
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
- 我们通过打印主线程中的RunLoop对象可以看到自动释放池何时创建和释放
<CFRunLoopObserver 0x7fa3fbf22d20 [0x10d958180]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 2147483647, callout = _wrapRunLoopWithAutoreleasePoolHandler (0x10daa1a03), context = <CFArray 0x7fa3fbf2b330 [0x10d958180]>{type = mutable-small, count = 0, values = ()}}
<CFRunLoopObserver 0x7fa3fbf22c80 [0x10d958180]>{valid = Yes, activities = 0x1, repeats = Yes, order = -2147483647, callout = _wrapRunLoopWithAutoreleasePoolHandler (0x10daa1a03), context = <CFArray 0x7fa3fbf2b330 [0x10d958180]>{type = mutable-small, count = 0, values = ()}}
- 上述代码可以看到在观察者监听RunLoop状态时,对自动释放池有操作(_wrapRunLoopWithAutoreleasePoolHandler)
- 对应RunLoop状态为activities = 0xa0(160),activities = 0x1(1)
- 由于RunLoop状态有以下几种
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0), 1(即将进入RunLoop)
kCFRunLoopBeforeTimers = (1UL << 1), 2 (即将处理Timer事件)
kCFRunLoopBeforeSources = (1UL << 2), 4(即将处理输入源事件)
kCFRunLoopBeforeWaiting = (1UL << 5), 32(即将进入睡眠)
kCFRunLoopAfterWaiting = (1UL << 6), 64(即将醒来)
kCFRunLoopExit = (1UL << 7), 128(即将退出RunLoop)
kCFRunLoopAllActivities = 0x0FFFFFFFU(所有状态)
};
- 可以看到在activities = 0xa0(160)时,即在kCFRunLoopBeforeWaiting和kCFRunLoopExit阶段,会有自动释放池的操作,在activities = 0x1(1),即kCFRunLoopEntry阶段,也会进行自动释放池的操作
- 综合分析:
- 程序在即将进入RunLoop时会创建一个自动释放池
- 即将进入休眠 1.销毁一个自动释放池 2.再创建一个新的自动释放池
- 程序即将退出时,会销毁一个自动释放池