iOS中的RunLoop(常用场景)

171 阅读2分钟

这是我参与8月更文挑战的第27天,活动详情查看:8月更文挑战

开启一个常驻线程(让一个子线程不进入消亡状态,等待其他线程发来消息,处理事件)

在子线程中,当任务执行完毕之后子线程就会被销毁,如果我们需要开启一个子线程,在程序运行过程中永远都存在,那么我们就会面临一个问题,如何让子线程永远活着,这时就要用到常驻线程。所谓常驻线程只需在这个线程的mode中添加(SourceTimerObserver任意一个即可),那么这个线程中的 RunLoop 就可以跑起来,当需要将 RunLoop 停掉的时候,直接调用类似- (void)removePort:(NSPort *)aPort forMode:(NSRunLoopMode)mode方法移除即可。

  • 通过在 RunLoop 中添加 Timer 实现常驻线程

    @interface ThreadViewController ()
    @property (strong, nonatomic) NSThread *thread;
    @end
    
    @implementation ThreadViewController
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.thread = [[NSThread alloc]initWithTarget:self selector:@selector(run) object:nil];
        [self.thread start];
    }
    
    - (void)run{
    
        //NSThred创建额子线程需要手动管理内存
        @autoreleasepool{
            [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(log) userInfo:nil repeats:YES];
            //启动runloop
            NSLog(@"runloop---start--");
            [[NSRunLoop currentRunLoop] run];
            NSLog(@"runloop---end--");
        }
    }
    
    - (void)log{
        NSLog(@"%@",[NSThread currentThread]);
    }
    

    log:

    log.png

  • 通过在 RunLoop 中添加 Source 实现常驻线程

    @interface ThreadViewController ()
    @property (strong, nonatomic) NSThread *thread;
    @end
    
    @implementation ThreadViewController
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.thread = [[NSThread alloc]initWithTarget:self selector:@selector(run) object:nil];
        [self.thread start];
    }
    
    - (void)run{
    
        @autoreleasepool{
            //在当前子线程的runloop中的mode中添加端口(其实就是加了source,有了source,runloop就可以跑起来)
            [[NSRunLoop currentRunLoop]addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
            //启动runloop
            NSLog(@"runloop---start--");
            [[NSRunLoop currentRunLoop] run];
            NSLog(@"runloop---end--");
        }
    }
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        //在 self.thread 线程中调用log
        [self performSelector:@selector(log) onThread:self.thread withObject:nil waitUntilDone:YES];
    }
    
    - (void)log{
        NSLog(@"%@",[NSThread currentThread]);
    }
    

    log: log

可以让某些事件在特定模式下执行

网络下载的图片,如果显示在 UIScrollView、UITableView上,当滑动屏幕的时候,UI有时会卡顿,解决这个问题可以用 RunLoop

//假如后台下载图片已经完成,此刻仍在滑动屏幕,那么可以延迟显示,当你停止滑动屏幕的时候再显示,此刻只需将runloop模式改为NSDefaultRunLoopMode。
- (void)viewDidLoad {
    [super viewDidLoad];
    [self showImage];
}

- (void)showImage{
    //只在NSDefaultRunLoopMode模式下显示
    [self.imageView performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@"aa"] afterDelay:3 inModes:@[NSDefaultRunLoopMode]];
}

添加Observer控制事件响应

可以添加 Observer 监听 RunLoop 的状态,比如监听点击事件的处理(在所有点击事件之前做一些事情)

- (void)observer{
    /*
    添加Observer
    参数1:默认值
    参数2:监听活动(这里监听了kCFRunLoopAllActivities)
    参数3:重复
    参数4:0
    */

    CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
        NSLog(@"---监听Runloop状态改变--%lu",activity);
    });

    /*
    添加观察者,监听Runloop的在kCFRunLoopDefaultMode模式下的状态
    参数1:当前runloop
    参数2:监听者
    参数3:模式
    */
    CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
    //释放Observer
    CFRelease(observer);
}

log: log