iOS课程观看笔记(七)---RunLoop

在这里插入图片描述

GCD的内部的线程池相关问题
RunLoop与RunTime

什么是RunLoop?

RunLoop是通过内部维护的事件循环来对 事件/消息 进行管理的 对象
事件循环包括两点:
没有消息需要处理时,休眠以避免资源占用
有消息需要处理时,立刻被唤醒

在这里插入图片描述

RunLoop的关键点,就是用户态与内核态的切换

问:为什么iOS中main()函数可以保持一直运行状态。而不退出?

在这里插入图片描述

在main()函数内部,会调用UIApplicationMain()函数,在UIApplicationMain()函数内部会启动主线程的RunLoop,RunLoop通过内部维护的事件循环来对 事件/消息 进行管理,能做到有事做的时候去做事,没事做的时候,通过用户态到内核态的切换,进行休眠,从而避免资源浪费。

RunLoop如何维护事件循环机制呢?


RunLoop的数据结构

苹果提供了两套RunLoop:NSRunLoop和CFRunLoop

CFRunLoop是Core Foundation框架的,CF开头的框架是开源的
NSRunLoop是Foundation框架的

NSRunLoop是对CFRunLoop的封装,提供了面向对象的API

CFRunLoop
CFRunLoopModel
Source/Timer/Observer

苹果CFRunLoop开源地址

在这里插入图片描述

CFRunLoopSource

Source0
需要手动唤醒线程
触摸事件处理
performSelector:onThread:

Source1
具备唤醒线程的能力
基于Port的线程间通信
系统事件捕捉

CFRunLoopTimer

基于事件的定时器
和NSTimer是 toll-free bridged(免费桥转换)的

CFRunLoopObserver

问:我们可以监听RunLoop的哪些时间点?

在这里插入图片描述

kCFRunLoopBeforeWaiting:waiting等待、休眠。是用户态到内核态的转换
kCFRunLoopAfterWaiting:是内核态到用户态的转换

在这里插入图片描述
线程和RunLoop是一对一的关系
RunLoop跟Model是一对多的关系
Model和Source/Timer/Observer也是一对多的关系

一个RunLoop包含若干个Mode,每个Mode又包含若干个source0、source1、observer、timer
RunLoop启动时只能选择其中一个Mode作为currentMode。
如果需要切换Mode,只能退出当前loop,重新选择一个Mode进入。
不同组的source0、source1、observer、timer能分隔开来,互不影响
如果Mode里没有任何source0、source1、observer、timer,RunLoop会立马退出

CFRunLoopCommonModes

CFRunLoopCommonModes并不是实际存在的一种Mode
commonMode是同步Source/Timer/Observer到多个Mode中的一种技术方案

问:点击App图标,从启动到杀死进程,中间经历了哪些?

从大的方面,经历了程序启动的三个方面:dyld、runtime、main函数
还可以从RunLoop的角度去分析

在这里插入图片描述
在这里插入图片描述

RunLoop和NSTimer

在这里插入图片描述
两种方法:
方法一:

NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
        NSLog(@"123");
    }];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

方法二:

void CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFRunLoopMode mode);

RunLoop与多线程

线程是和RunLoop是一一对应的
自己手动创建的线程默认是没有RunLoop的

问:怎样实现一个常驻线程?

为当前线程开启一个RunLoop
向该RunLoop中添加一个Port或者Source维护RunLoop的事件循环
启动该RunLoop

#import "MCObject.h"

@implementation MCObject

static NSThread *thread = nil;
// 标记是否要继续事件循环
static BOOL runAlways = YES;

+ (NSThread *)threadForDispatch{
    if (thread == nil) {
        @synchronized(self) {
            if (thread == nil) {
                // 线程的创建
                thread = [[NSThread alloc] initWithTarget:self selector:@selector(runRequest) object:nil];
                [thread setName:@"com.imooc.thread"];
                //启动
                [thread start];
            }
        }
    }
    return thread;
}

+ (void)runRequest
{
    // 创建一个Source
    CFRunLoopSourceContext context = {0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
    CFRunLoopSourceRef source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
    
    // 创建RunLoop,同时向RunLoop的DefaultMode下面添加Source
    CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
    
    // 如果可以运行
    while (runAlways) {
        @autoreleasepool {
            // 令当前RunLoop运行在DefaultMode下面
            CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e10, true);
        }
    }
    
    // 某一时机 静态变量runAlways = NO时 可以保证跳出RunLoop,线程退出
    CFRunLoopRemoveSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
    CFRelease(source);
}

@end

在这里插入图片描述

问:怎么保证子线程数据回来更新UI的时候,不打断用户的滑动操作?

网络数据请求通常在子线程中运行,以避免阻塞主线程。但是,由于只有主线程可以进行 UI 更新,所以在数据加载完成后需要回到主线程来更新 UI。如果在主线程中直接更新 UI,可能会导致 UI 布局计算和绘制操作打断正在进行的用户滑动操作。

平时App的主运行循环是在kCFRunLoopDefaultMode下执行的,当用户触发滑动操作时,主运行循环会切换到UITrackingRunLoopMode下执行,以保证滑动操作的流畅性。

滑动结束后,Runloop由UITrackingRunloopMode又回到kCFRunLoopDefaultMode下了。

所以,可以在将数据请求的结果回调切换到主线程中执行的时候,将其切换到NSDefaultRunLoopMode,这样就不会在用户滑动的时候进行 UI 更新,从而保证滑动的流畅性

即:将子线程数据给主线程刷新UI的时候,包装后提交到主线程的defaultModel下,这样两个model不会同时执行,也就不会打断用户的滑动操作。

代码示例:

dispatch_async(dispatch_get_global_queue(0, 0), ^{
    // 在这里下载数据...
    NSData *data = ... // 下载的数据
    // 回到主线程
    dispatch_async(dispatch_get_main_queue(), ^{
        [self.imageView performSelector:@selector(setImage:) 
                             withObject:[UIImage imageWithData:data] 
                             afterDelay:0 
                                inModes:@[NSDefaultRunLoopMode]];
    });
	
	//或者	
    //[self performSelectorOnMainThread:@selector(updateUI) withObject:nil waitUntilDone:NO modes:@[NSDefaultRunLoopMode]];
});

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值