多线程 - 13.RunLoop应用场景

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];
    // 使用timer开头的类方法创建定时器
    NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(demo) userInfo:nil repeats:YES];
    // 使用scheduledTimer类方法创建定时器
//  [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(demo) userInfo:nil repeats:YES];
    // NSDefaultRunLoopMode模式
//    [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];

    // 模式UITrackingRunLoopMode
//    [[NSRunLoop mainRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];

    // 模式NSRunLoopCommonModes(定时器运行时处于哪种模式就输出哪种模式)
    [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}
-(void)demo{
    NSLog(@"%s",__func__);
    NSLog(@"%@",[NSRunLoop mainRunLoop].currentMode);
}
  • 可以通过判断输出结果看到当前的模式

2.ImageView显示

  • 能够在RunLoop的拖拽模式情况下显示图片
- (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__);
    // 在子线程中添加RunLoop对象
    // 创建RunLoop对象
    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对象可以看到自动释放池何时创建和释放
// 状态1
 <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状态有以下几种
/* Run Loop Observer Activities */
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.再创建一个新的自动释放池
    • 程序即将退出时,会销毁一个自动释放池
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值