RunLoop

基本作用

保持程序的持续运行(比如主运行循环)

处理App中的各种事件(比如触摸事件、定时器事件、Selector事件)

节省CPU资源,提高程序性能:该做事时做事,该休息时休息


RunLoop与线程

每条线程都有唯一的一个与之对应的RunLoop对象

主线程的RunLoop已经自动创建好了,子线程的RunLoop需要主动创建

RunLoop在第一次获取时创建,在线程结束时销毁


一个 RunLoop 包含若干个 Mode,每个 Mode 又包含若干个 Source/Timer/Observer。每次调用 RunLoop 的主函数时,只能指定其中一个 Mode,这个Mode被称作 CurrentMode。如果需要切换 Mode,只能退出 Loop,再重新指定一个 Mode 进入。这样做主要是为了分隔开不同组的 Source/Timer/Observer,让其互不影响。

CFRunLoopSourceRef是事件产生的地方。Source有两个版本:Source0 和 Source1。

• Source0 只包含了一个回调(函数指针),它并不能主动触发事件。使用时,你需要先调用 CFRunLoopSourceSignal(source),将这个 Source 标记为待处理,然后手动调用 CFRunLoopWakeUp(runloop) 来唤醒 RunLoop,让其处理这个事件。

• Source1 包含了一个 mach_port 和一个回调(函数指针),被用于通过内核和其他线程相互发送消息。这种 Source 能主动唤醒 RunLoop 的线程,其原理在下面会讲到。

CFRunLoopTimerRef是基于时间的触发器,它和 NSTimer 是toll-free bridged 的,可以混用。其包含一个时间长度和一个回调(函数指针)。当其加入到 RunLoop 时,RunLoop会注册对应的时间点,当时间点到时,RunLoop会被唤醒以执行那个回调。

CFRunLoopObserverRef是观察者,每个 Observer 都包含了一个回调(函数指针),当 RunLoop 的状态发生变化时,观察者就能通过回调接受到这个变化。可以观测的时间点有以下几个:

typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {

kCFRunLoopEntry = (1UL << 0), // 即将进入Loop

kCFRunLoopBeforeTimers= (1UL << 1), // 即将处理 Timer

kCFRunLoopBeforeSources = (1UL << 2), // 即将处理 Source

kCFRunLoopBeforeWaiting = (1UL << 5), // 即将进入休眠

kCFRunLoopAfterWaiting= (1UL << 6), // 刚从休眠中唤醒

kCFRunLoopExit= (1UL << 7), // 即将退出Loop

};


RunLoop应用

NSTimer

ImageView显示

PerformSelector

常驻线程

自动释放池


- (void)timer

{

    NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];

    // 定时器只运行在NSDefaultRunLoopMode下,一旦RunLoop进入其他模式,这个定时器就不会工作

    //    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];

    // 定时器只运行在UITrackingRunLoopMode下,一旦RunLoop进入其他模式,这个定时器就不会工作

    //    [[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];

    // 定时器会跑在标记为common modes的模式下

    // 标记为common modes的模式:UITrackingRunLoopMode和NSDefaultRunLoopMode兼容

    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

}

- (void)timer2

{

    // 调用了scheduledTimer返回的定时器,已经自动被添加到当前runLoop中,而且是NSDefaultRunLoopMode

    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];

    // 修改模式

    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

}

2.ImageView

需求:当用户在拖拽时(UI交互时)不显示图片,拖拽完成时显示图片

方法1 监听UIScrollerView滚动 (通过UIScrollViewDelegate监听,此处不再举例)

方法2 RunLoop 设置运行模式

// 只在NSDefaultRunLoopMode模式下显示图片

[self.imageView performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@"placeholder"] afterDelay:3.0 inModes:@[NSDefaultRunLoopMode]];

3.PerformSelector

PerformSelector

inModes:设置运行模式

4.常驻线程 (重要)

应用场景:经常在后台进行耗时操作,如:监控联网状态,扫描沙盒等 不希望线程处理完事件就销毁,保持常驻状态

第一种(推荐)

开启

- (void)run

{

  //addPort:添加端口(就是source)  forMode:设置模式

   [[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode];

  //启动RunLoop

    [[NSRunLoop currentRunLoop] run];

 /*

  //另外两种启动方式

    [NSDate distantFuture]:遥远的未来  这种写法跟上面的run是一个意思

    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];

    不设置模式

    [[NSRunLoop currentRunLoop] runUntilDate:[NSDate distantFuture]];

  */

}

退出-退出当前线程

1[NSThread exit];


在休眠前(kCFRunLoopBeforeWaiting)进行释放,处理事件前创建释放池,中间创建的对象会放入释放池

特别注意:在启动RunLoop之前建议用 @autoreleasepool {...}包裹

意义:创建一个大释放池,释放{}期间创建的临时对象,一般好的框架的作者都会这么做

- (void)execute

{

    @autoreleasepool {

        NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];

        [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];

        [[NSRunLoop currentRunLoop] run];

    }

}

你可能感兴趣的:(RunLoop)